From 7f54a9798cf071eecc5e64d1e97f7ea4ee302eb4 Mon Sep 17 00:00:00 2001 From: Michael Leuschel <leuschel@cs.uni-duesseldorf.de> Date: Mon, 4 Jun 2018 23:17:03 +0200 Subject: [PATCH] update presentation --- notebooks/presentations/SETS_RODIN18.ipynb | 612 ++++++++++++++---- .../presentations/img/inputoutputflow.pdf | Bin 0 -> 14906 bytes 2 files changed, 485 insertions(+), 127 deletions(-) create mode 100644 notebooks/presentations/img/inputoutputflow.pdf diff --git a/notebooks/presentations/SETS_RODIN18.ipynb b/notebooks/presentations/SETS_RODIN18.ipynb index 7004a21..dba64d2 100644 --- a/notebooks/presentations/SETS_RODIN18.ipynb +++ b/notebooks/presentations/SETS_RODIN18.ipynb @@ -121,7 +121,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 14, "metadata": { "slideshow": { "slide_type": "-" @@ -132,11 +132,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "[2018-06-04 13:54:14,028, T+23877] \"Shell-0\" de.prob.cli.PrologProcessProvider.makeProcess(PrologProcessProvider.java:64): [INFO] Starting ProB's Prolog Core. Path is /Users/leuschel/git_root/prob_prolog/probcli.sh\n", - "[2018-06-04 13:54:15,603, T+25452] \"Shell-0\" de.prob.cli.PortPattern.setValue(PortPattern.java:30): [INFO] Server has started and listens on port 62613\n", - "[2018-06-04 13:54:15,605, T+25454] \"Shell-0\" de.prob.cli.InterruptRefPattern.setValue(InterruptRefPattern.java:29): [INFO] Server can receive user interrupts via reference 47762\n", - "[2018-06-04 13:54:15,609, T+25458] \"ProB Output Logger for instance 1ede3ed1\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] -- starting command loop --\u001b[0m\n", - "[2018-06-04 13:54:15,624, T+25473] \"ProB Output Logger for instance 1ede3ed1\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] Connected: 127.0.0.1\u001b[0m\n" + "[2018-06-04 17:10:21,614, T+7877565] \"Shell-0\" de.prob.cli.PrologProcessProvider.makeProcess(PrologProcessProvider.java:64): [INFO] Starting ProB's Prolog Core. Path is /Users/leuschel/git_root/prob_prolog/probcli.sh\n", + "[2018-06-04 17:10:22,773, T+7878724] \"Shell-0\" de.prob.cli.PortPattern.setValue(PortPattern.java:30): [INFO] Server has started and listens on port 57165\n", + "[2018-06-04 17:10:22,774, T+7878725] \"Shell-0\" de.prob.cli.InterruptRefPattern.setValue(InterruptRefPattern.java:29): [INFO] Server can receive user interrupts via reference 52908\n", + "[2018-06-04 17:10:22,782, T+7878733] \"ProB Output Logger for instance 17752aaa\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] -- starting command loop --\u001b[0m\n", + "[2018-06-04 17:10:22,792, T+7878743] \"ProB Output Logger for instance 17752aaa\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] Connected: 127.0.0.1\u001b[0m\n" ] }, { @@ -145,7 +145,7 @@ "Loaded machine: MyBasicSets : []\n" ] }, - "execution_count": 4, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -741,7 +741,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "source": [ "Function application:" ] @@ -761,7 +765,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "source": [ "Relational inverse (.~) and relational image .[.] :" ] @@ -985,7 +993,17 @@ "source": [ "## Constraint Solving Applications ##\n", "Constraint solving has many applications in formal methods in general and B in particular.\n", - "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ "#### Animation ####\n", "It is required to animate implicit specifications.\n", "Take for example an event\n", @@ -1150,7 +1168,11 @@ { "cell_type": "code", "execution_count": 32, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "outputs": [ { "data": { @@ -1173,7 +1195,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -1192,13 +1214,13 @@ "\tO = 0" ] }, - "execution_count": 33, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "{S,E,N,D, M,O,R, Y} <: 0..9 & S >0 & M >0 & \n", + "{S,E,N,D, M,O,R, Y} ⊆ 0..9 & S >0 & M >0 & \n", " card({S,E,N,D, M,O,R, Y}) = 8 & \n", " S*1000 + E*100 + N*10 + D +\n", " M*1000 + O*100 + R*10 + E =\n", @@ -1207,7 +1229,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "source": [ "We can find all solutions (to the unmodified puzzle) using a set comprehension and make sure that there is just a single soltuion:" ] @@ -1237,6 +1263,37 @@ " M*10000 + O*1000 + N*100 + E*10 + Y }" ] }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "|Nr|S|E|N|D|M|O|R|Y|\n", + "|---|---|---|---|---|---|---|---|---|\n", + "|1|9|5|6|7|1|0|8|2|\n" + ], + "text/plain": [ + "Nr\tS\tE\tN\tD\tM\tO\tR\tY\n", + "1\t9\t5\t6\t7\t1\t0\t8\t2\n" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + ":table {S,E,N,D, M,O,R, Y |\n", + " {S,E,N,D, M,O,R, Y} <: 0..9 & S >0 & M >0 & \n", + " card({S,E,N,D, M,O,R, Y}) = 8 & \n", + " S*1000 + E*100 + N*10 + D +\n", + " M*1000 + O*100 + R*10 + E =\n", + " M*10000 + O*1000 + N*100 + E*10 + Y }" + ] + }, { "cell_type": "markdown", "metadata": { @@ -1331,13 +1388,27 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "source": [ "### Booleans ###\n", "\n", "If we have only booleans, constraint solving is equivalent to SAT solving.\n", "Internally, ProB has an interpreter which does **not** translate to CNF (conjunctive normal form), but is otherwise similar to DPLL: deterministic propagations are carried out first (unit propagation) and there are heuristics to choose the next boolean variable to enumerate.\n", - "(We do not translate to CNF also because of well-definedness issues.)\n", + "(We do not translate to CNF also because of well-definedness issues.)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ "\n", "#### Knights and Knave Puzzle####\n", "Here is a puzzle from Smullyan involving an island with only knights and knaves.\n", @@ -1356,7 +1427,11 @@ { "cell_type": "code", "execution_count": 37, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -1391,45 +1466,7 @@ "\n", "Let us take the integer constraint ```x*x=100``` which we saw earlier.\n", "This constraint is actually more complicated than might appear at first sight: the constraint is not linear and the domain of x is not bounded. Indeed, B supports mathematical integers without any bit size restriction.\n", - "So, let us first look at some simpler constraints, where the domains of the variables are all bounded.\n", - "Let us consider a small arithmetic puzzle \n", - "```\n", - " X Y\n", - "+ X Y\n", - "-----\n", - " Y 0\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "TRUE\n", - "\n", - "Solution:\n", - "\tX = 2\n", - "\tY = 5" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "X:1..9 & Y:0..9 & X*10 + Y + X*10 + Y = Y*10" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Given that we know the domain of X and Y one can represent the integers by binary numbers and convert the constraint to a SAT problem." + "So, let us first look at some simpler constraints, where the domains of the variables are all bounded." ] }, { @@ -1440,7 +1477,8 @@ } }, "source": [ - "Let us look at an even simpler constraint X:0..3 & Y:0..3 & X+Y=2. As you can see there are three solutions for this constraint:\n" + "Let us look at the simple constraint ```X:0..3 & Y:0..3 & X+Y=2```.\n", + "As you can see there are three solutions for this constraint:\n" ] }, { @@ -1472,11 +1510,15 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "#### Solving constraints by translating to SAT ####\n", - "Given that we know the domain of X and Y in the constraint ``` X:0..3 & Y:0..3 & X+Y=2``` one can represent the integers by binary numbers and convert the constraint to a SAT problem.\n", - "The number 2 is 10 in binary and we can represent X and Y each by two bits X0,X1 and Y0,Y1.\n", + "Given that we know the *domain* of X and Y in the constraint ``` X:0..3 & Y:0..3 & X+Y=2```, we can represent the integers by binary numbers and convert the constraint to a **SAT** problem.\n", + "The number 2 is ```10``` in binary and we can represent X and Y each by two bits X0,X1 and Y0,Y1.\n", "We can translate the addition to a propositional logic formula:\n", "\n", "Bit1 | Bit0\n", @@ -1486,7 +1528,7 @@ " |\n", " Z1 | Z0\n", " \n", - "Let us find one solution to this constraint, by encoding addition using an additional carry bit:" + "Let us find one solution to this constraint, by encoding addition using an additional carry bit ```CARRY0```:" ] }, { @@ -1525,7 +1567,11 @@ { "cell_type": "code", "execution_count": 41, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "outputs": [ { "data": { @@ -1557,7 +1603,11 @@ { "cell_type": "code", "execution_count": 42, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -1602,7 +1652,11 @@ { "cell_type": "code", "execution_count": 43, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "outputs": [ { "name": "stdout", @@ -1676,12 +1730,28 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "#### Translation to SMTLib ####\n", - "At iFM'2016 we (Krings, L.) presented a translation to SMTLib format to use either the Z3 or CVC4 SMT solver.\n", - "Compared to the Kodkod backend, no translation to SAT is performed, SMTLib supports integer predicates and operators in the language.\n", - "Here is ProB's SMTLib/Z3 API calls for the constraint:\n", + "At iFM'2016 we (Krings, L.) presented a translation to SMTLib format to use either the **Z3** or **CVC4** SMT solver.\n", + "Compared to the Kodkod backend, no translation to SAT is performed, SMTLib supports **integer** predicates and integer operators in the language.\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "Here is ProB's SMTLib/Z3 API calls for the constraint ``` X:0..3 & Y:0..3 & X+Y=2```:\n", "- mk_var(integer,x) $\\rightarrow$ 2\n", "- mk_var(integer,y) $\\rightarrow$ 3\n", "- mk_int_const(0) $\\rightarrow$ 4\n", @@ -1726,7 +1796,7 @@ "cell_type": "markdown", "metadata": { "slideshow": { - "slide_type": "subslide" + "slide_type": "slide" } }, "source": [ @@ -1749,11 +1819,34 @@ " - x=2 $\\leadsto$ x:2..2, y:0..0" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "Let us now examine the three approaches for the KISS-PASSION puzzle:" + ] + }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 17, "metadata": {}, "outputs": [ + { + "data": { + "text/markdown": [ + "Execution time: 0.050491290 seconds" + ], + "text/plain": [ + "Execution time: 0.050491290 seconds" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/plain": [ @@ -1769,46 +1862,63 @@ "\tO = 8" ] }, - "execution_count": 46, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ - ":solve prob {K,P} <: 1..9 & {I,S,A,O,N} <: 0..9 & (1000*K+100*I+10*S+S) * (1000*K+100*I+10*S+S) = 1000000*P+100000*A+10000*S+1000*S+100*I+10*O+N & card({K, I, S, P, A, O, N}) = 7" + ":time :solve prob {K,P} <: 1..9 & {I,S,A,O,N} <: 0..9 & (1000*K+100*I+10*S+S) * (1000*K+100*I+10*S+S) = 1000000*P+100000*A+10000*S+1000*S+100*I+10*O+N & card({K, I, S, P, A, O, N}) = 7" ] }, { "cell_type": "code", - "execution_count": 47, - "metadata": {}, + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "outputs": [ { "ename": "CommandExecutionException", - "evalue": ":solve: Computation not completed: no solution found (but one might exist)", + "evalue": ":time: :solve: Computation not completed: no solution found (but one might exist)", "output_type": "error", "traceback": [ - "\u001b[1m\u001b[31m:solve: Computation not completed: no solution found (but one might exist)\u001b[0m" + "\u001b[1m\u001b[31m:time: :solve: Computation not completed: no solution found (but one might exist)\u001b[0m" ] } ], "source": [ - ":solve z3 {K,P} <: 1..9 & {I,S,A,O,N} <: 0..9 & (1000*K+100*I+10*S+S) * (1000*K+100*I+10*S+S) = 1000000*P+100000*A+10000*S+1000*S+100*I+10*O+N & card({K, I, S, P, A, O, N}) = 7" + ":time :solve z3 {K,P} <: 1..9 & {I,S,A,O,N} <: 0..9 & (1000*K+100*I+10*S+S) * (1000*K+100*I+10*S+S) = 1000000*P+100000*A+10000*S+1000*S+100*I+10*O+N & card({K, I, S, P, A, O, N}) = 7" ] }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[2018-06-04 14:01:42,085, T+471934] \"ProB Output Logger for instance 1ede3ed1\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] kodkod ok: K : 1 .. 9 & P : 1 .. 9 & I : 0 .. 9 & S : 0 .. 9 ... ints: irange(0,99980001), intatoms: irange(0,9)\u001b[0m\n", - "[2018-06-04 14:01:42,086, T+471935] \"ProB Output Logger for instance 1ede3ed1\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] Times for computing solutions: [373]\u001b[0m\n" + "[2018-06-04 21:43:31,067, T+24267018] \"ProB Output Logger for instance 17752aaa\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] kodkod ok: K : 1 .. 9 & P : 1 .. 9 & I : 0 .. 9 & S : 0 .. 9 ... ints: irange(0,99980001), intatoms: irange(0,9)\u001b[0m\n", + "[2018-06-04 21:43:31,069, T+24267020] \"ProB Output Logger for instance 17752aaa\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] Kodkod module started up successfully (SAT solver SAT4J with timeout of 1500 ms).\u001b[0m\n", + "[2018-06-04 21:43:31,069, T+24267020] \"ProB Output Logger for instance 17752aaa\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] Times for computing solutions: [636]\u001b[0m\n" ] }, + { + "data": { + "text/markdown": [ + "Execution time: 2.287290784 seconds" + ], + "text/plain": [ + "Execution time: 2.287290784 seconds" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/plain": [ @@ -1824,18 +1934,22 @@ "\tO = 8" ] }, - "execution_count": 48, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ - ":solve kodkod {K,P} <: 1..9 & {I,S,A,O,N} <: 0..9 & (1000*K+100*I+10*S+S) * (1000*K+100*I+10*S+S) = 1000000*P+100000*A+10000*S+1000*S+100*I+10*O+N & card({K, I, S, P, A, O, N}) = 7" + ":time :solve kodkod {K,P} <: 1..9 & {I,S,A,O,N} <: 0..9 & (1000*K+100*I+10*S+S) * (1000*K+100*I+10*S+S) = 1000000*P+100000*A+10000*S+1000*S+100*I+10*O+N & card({K, I, S, P, A, O, N}) = 7" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "source": [ "Result for KISS*KISS=PASSION puzzle:\n", "\n", @@ -1848,7 +1962,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "### Unbounded integers ###\n", "The SAT translation via Kodkod/Alloy requires to determine the bid width.\n", @@ -2020,7 +2138,8 @@ } }, "source": [ - "Here is an inconsistency which cannot be detected by CLP(FD)'s interval propagation. ProB can detect it with CHR enabled, but without the module the result is **UNKNOWN**." + "Here is an inconsistency which cannot be detected by CLP(FD)'s interval propagation.\n", + "ProB can detect it with CHR (Constraint Handling Rules) enabled, but without the module the result is **UNKNOWN**." ] }, { @@ -2072,7 +2191,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "source": [ "### Summary for Integer Arithmetic ###\n", "\n", @@ -2195,7 +2318,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "source": [ "This translation to SAT is exactly what the Kodkod backend does:" ] @@ -2299,7 +2426,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "source": [ "Also, in the following constraint, the set x is unbounded and no translation to SAT is feasible (without a very clever analysis of the universal implications)." ] @@ -2327,6 +2458,61 @@ "{100}:x & !y.(y:x => (!z.(z:y => y \\/ {z / 2}:x)))" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "However, when it is applicable the propositional encoding of sets can be very effective for SAT solvers.\n", + "The following example, involving various relational operators can currently only be solved by our Kodkod backend:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[2018-06-04 21:54:17,800, T+24913751] \"ProB Output Logger for instance 17752aaa\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] kodkod ok: r : 1 .. 5 <-> 1 .. 5 & (r ; r) = r & r /= {} & do... ints: irange(0,5), intatoms: irange(0,5)\u001b[0m\n", + "[2018-06-04 21:54:17,801, T+24913752] \"ProB Output Logger for instance 17752aaa\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] Times for computing solutions: [2,2,1,1,3,2,1,2,1,1,5,1,1,2,1,2,1,2,1,1,1,1]\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "Execution time: 0.153314218 seconds" + ], + "text/plain": [ + "Execution time: 0.153314218 seconds" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "TRUE\n", + "\n", + "Solution:\n", + "\tr = {(3↦5),(3↦4),(5↦5),(5↦4),(1↦4),(2↦4),(4↦4)}" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + ":time :solve kodkod r: 1..5 <-> 1..5 & (r;r) = r & r /= {} & dom(r)=1..5 & r[2..3]=4..5" + ] + }, { "cell_type": "markdown", "metadata": { @@ -2375,7 +2561,11 @@ { "cell_type": "code", "execution_count": 64, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -2437,7 +2627,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "source": [ "This can be solved by Z3 but not by CVC4. Already the slightly more complicated example from above (or the other examples) cannot be solved:" ] @@ -2525,7 +2719,11 @@ { "cell_type": "code", "execution_count": 67, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "outputs": [ { "data": { @@ -2554,7 +2752,11 @@ { "cell_type": "code", "execution_count": 68, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "outputs": [ { "name": "stdout", @@ -2681,7 +2883,6 @@ }, "source": [ "### ProB's Set Solver ###\n", - "\n", "ProB has actually three set representations:\n", "- Prolog lists of elements\n", "- AVL trees for fully known sets\n", @@ -2696,7 +2897,6 @@ "The list representation is used for sets where some of the members are known or partially known.\n", "\n", "#### AVL tree representation ####\n", - "\n", "The following generates the AVL tree representation:" ] }, @@ -2871,7 +3071,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "source": [ "The virtual timeout message can be removed (and performance improved) by adding the symbolic pragma:" ] @@ -2916,7 +3120,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "source": [ "#### List representation ####\n", "The list representation is used when a finite set is partially known and constraint solving has to determine the set.\n" @@ -2927,7 +3135,7 @@ "execution_count": 78, "metadata": { "slideshow": { - "slide_type": "subslide" + "slide_type": "fragment" } }, "outputs": [ @@ -3148,7 +3356,11 @@ { "cell_type": "code", "execution_count": 91, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "outputs": [ { "ename": "CommandExecutionException", @@ -3389,7 +3601,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "### Relation to SETLOG ###\n", "\n", @@ -3435,7 +3651,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "source": [ "Another example from that paper is\n", "```\n", @@ -3611,7 +3831,11 @@ { "cell_type": "code", "execution_count": 102, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "outputs": [ { "name": "stdout", @@ -3658,13 +3882,34 @@ "- ProB's default backend:\n", " - can deal with unbounded and large sets\n", " - limited constraint solving for complex constraints involving image, transitive closure,...\n", + " - no learning, backjumping\n", " - works well for large sets and semi-deterministic computation\n", " - works well for animation, data validation, disproving\n", - " - limitations trigger for symbolic model checking (IC3,...)\n", - " - Future work: improve combination with Z3, improve list representation (maybe use a bit-vector like representation) \n", + " - limitations appear for symbolic model checking (IC3,...)\n", + " - Future work: improve combination with Z3/Kodkod, improve list representation (maybe use a bit-vector like representation, and CLP(FD) cardinality variable) \n", " " ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "### Integrations of Approaches ###\n", + "\n", + "ProB provides the joint application of the CLP(FD) and SMT backend (preference ```SMT_SUPPORTED_INTERPRETER```.\n", + "Constraints are posted both to ProB and Z3/CVC4, with the hope that Z3/CVC4 prune infeasible branches.\n", + "The main motivation was new symbolic validation techniques such as IC3.\n", + "\n", + "The Kodkod integration also passes higher-order/unbounded constraints to ProB, after solving the first order finite constraints with Kodkod/Alloy.\n", + "However, this kind of integration is rarely useful (most of the generated solutions get rejected by ProB).\n", + "\n", + "A more promising, fine-grained integration has been presented at PADL'18." + ] + }, { "cell_type": "markdown", "metadata": { @@ -3829,7 +4074,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "source": [ "In the following the inverse operator seems to pose problems to Z3:" ] @@ -3880,7 +4129,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "### Comparison ###\n", "Some further examples for comparison of the backends" @@ -4100,7 +4353,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "#### Graph theorem ####\n", "\n", @@ -4109,18 +4366,18 @@ }, { "cell_type": "code", - "execution_count": 117, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[2018-06-04 14:09:20,430, T+930279] \"Shell-0\" de.prob.cli.PrologProcessProvider.makeProcess(PrologProcessProvider.java:64): [INFO] Starting ProB's Prolog Core. Path is /Users/leuschel/git_root/prob_prolog/probcli.sh\n", - "[2018-06-04 14:09:21,622, T+931471] \"Shell-0\" de.prob.cli.PortPattern.setValue(PortPattern.java:30): [INFO] Server has started and listens on port 63550\n", - "[2018-06-04 14:09:21,622, T+931471] \"Shell-0\" de.prob.cli.InterruptRefPattern.setValue(InterruptRefPattern.java:29): [INFO] Server can receive user interrupts via reference 47978\n", - "[2018-06-04 14:09:21,624, T+931473] \"ProB Output Logger for instance 2d81a536\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] -- starting command loop --\u001b[0m\n", - "[2018-06-04 14:09:21,638, T+931487] \"ProB Output Logger for instance 2d81a536\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] Connected: 127.0.0.1\u001b[0m\n" + "[2018-06-04 16:46:59,166, T+6475117] \"Shell-0\" de.prob.cli.PrologProcessProvider.makeProcess(PrologProcessProvider.java:64): [INFO] Starting ProB's Prolog Core. Path is /Users/leuschel/git_root/prob_prolog/probcli.sh\n", + "[2018-06-04 16:47:00,347, T+6476298] \"Shell-0\" de.prob.cli.PortPattern.setValue(PortPattern.java:30): [INFO] Server has started and listens on port 55595\n", + "[2018-06-04 16:47:00,348, T+6476299] \"Shell-0\" de.prob.cli.InterruptRefPattern.setValue(InterruptRefPattern.java:29): [INFO] Server can receive user interrupts via reference 52301\n", + "[2018-06-04 16:47:00,350, T+6476301] \"ProB Output Logger for instance ecdd0f\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] -- starting command loop --\u001b[0m\n", + "[2018-06-04 16:47:00,364, T+6476315] \"ProB Output Logger for instance ecdd0f\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] Connected: 127.0.0.1\u001b[0m\n" ] }, { @@ -4129,7 +4386,7 @@ "Loaded machine: GraphTheorem : []\n" ] }, - "execution_count": 117, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -4194,7 +4451,11 @@ { "cell_type": "code", "execution_count": 120, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "outputs": [ { "name": "stdout", @@ -4249,7 +4510,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "source": [ "So with Kodkod we proved the theorem for 5 nodes in about a second.\n", "What if we remove the self-loops restriction:" @@ -4325,7 +4590,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "### Benchmark Puzzles ####\n", "\n", @@ -4435,7 +4704,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "source": [ "#### N-Bishops ####\n" ] @@ -4678,7 +4951,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "source": [ "#### Datavalidation example ####\n" ] @@ -4845,18 +5122,18 @@ }, { "cell_type": "code", - "execution_count": 139, + "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[2018-06-04 14:09:38,543, T+948392] \"Shell-0\" de.prob.cli.PrologProcessProvider.makeProcess(PrologProcessProvider.java:64): [INFO] Starting ProB's Prolog Core. Path is /Users/leuschel/git_root/prob_prolog/probcli.sh\n", - "[2018-06-04 14:09:39,778, T+949627] \"Shell-0\" de.prob.cli.PortPattern.setValue(PortPattern.java:30): [INFO] Server has started and listens on port 63572\n", - "[2018-06-04 14:09:39,779, T+949628] \"Shell-0\" de.prob.cli.InterruptRefPattern.setValue(InterruptRefPattern.java:29): [INFO] Server can receive user interrupts via reference 47990\n", - "[2018-06-04 14:09:39,781, T+949630] \"ProB Output Logger for instance 1361b11\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] -- starting command loop --\u001b[0m\n", - "[2018-06-04 14:09:39,798, T+949647] \"ProB Output Logger for instance 1361b11\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] Connected: 127.0.0.1\u001b[0m\n" + "[2018-06-04 15:03:05,856, T+241807] \"Shell-0\" de.prob.cli.PrologProcessProvider.makeProcess(PrologProcessProvider.java:64): [INFO] Starting ProB's Prolog Core. Path is /Users/leuschel/git_root/prob_prolog/probcli.sh\n", + "[2018-06-04 15:03:07,015, T+242966] \"Shell-0\" de.prob.cli.PortPattern.setValue(PortPattern.java:30): [INFO] Server has started and listens on port 51091\n", + "[2018-06-04 15:03:07,018, T+242969] \"Shell-0\" de.prob.cli.InterruptRefPattern.setValue(InterruptRefPattern.java:29): [INFO] Server can receive user interrupts via reference 50312\n", + "[2018-06-04 15:03:07,021, T+242972] \"ProB Output Logger for instance 5f836b9a\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] -- starting command loop --\u001b[0m\n", + "[2018-06-04 15:03:07,034, T+242985] \"ProB Output Logger for instance 5f836b9a\" de.prob.cli.ProBInstance.readAndLog(ConsoleListener.java:48): [INFO] Connected: 127.0.0.1\u001b[0m\n" ] }, { @@ -4865,7 +5142,7 @@ "Loaded machine: signals : []\n" ] }, - "execution_count": 139, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -5010,8 +5287,89 @@ }, { "cell_type": "code", - "execution_count": 142, + "execution_count": 5, "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "|Nr|prj1|prj2|\n", + "|---|---|---|\n", + "|1|PL01|PL02|\n", + "|2|PL02|PL03|\n", + "|3|PL03|PL04|\n", + "|4|PL04|PL05|\n", + "|5|PL05|PL06|\n", + "|6|PL06|PL07|\n", + "|7|PL07|PL08|\n", + "|8|PL08|PL09|\n", + "|9|PL09|PL10|\n", + "|10|PL10|PL11|\n", + "|11|PL11|PL12|\n", + "|12|PL12|PL13|\n", + "|13|PL13|PL14|\n", + "|14|PL14|PL15|\n", + "|15|PL15|PL16|\n", + "|16|PL16|PL17|\n", + "|17|PL17|PL18|\n", + "|18|PL18|PL19|\n", + "|19|PL19|PL20|\n", + "|20|PL20|PL20|\n", + "|21|PL30|PL31|\n", + "|22|PL31|PL32|\n", + "|23|PL33|PL34|\n", + "|24|PL34|PL35|\n" + ], + "text/plain": [ + "Nr\tprj1\tprj2\n", + "1\tPL01\tPL02\n", + "2\tPL02\tPL03\n", + "3\tPL03\tPL04\n", + "4\tPL04\tPL05\n", + "5\tPL05\tPL06\n", + "6\tPL06\tPL07\n", + "7\tPL07\tPL08\n", + "8\tPL08\tPL09\n", + "9\tPL09\tPL10\n", + "10\tPL10\tPL11\n", + "11\tPL11\tPL12\n", + "12\tPL12\tPL13\n", + "13\tPL13\tPL14\n", + "14\tPL14\tPL15\n", + "15\tPL15\tPL16\n", + "16\tPL16\tPL17\n", + "17\tPL17\tPL18\n", + "18\tPL18\tPL19\n", + "19\tPL19\tPL20\n", + "20\tPL20\tPL20\n", + "21\tPL30\tPL31\n", + "22\tPL31\tPL32\n", + "23\tPL33\tPL34\n", + "24\tPL34\tPL35\n" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + ":table {PL01 |-> PL02, PL02 |-> PL03, PL03 |-> PL04, PL04 |-> PL05,\n", + " PL05 |-> PL06, PL06 |-> PL07, PL07 |-> PL08, PL08 |-> PL09,\n", + " PL09 |-> PL10, PL10 |-> PL11, PL11 |-> PL12, PL12 |-> PL13,\n", + " PL13 |-> PL14, PL14 |-> PL15, PL15 |-> PL16, PL16 |-> PL17,\n", + " PL17 |-> PL18, PL18 |-> PL19, PL19 |-> PL20, PL20 |-> PL20,\n", + " PL30 |-> PL31, PL31 |-> PL32, PL33 |-> PL34, PL34 |-> PL35}" + ] + }, + { + "cell_type": "code", + "execution_count": 142, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "outputs": [ { "name": "stdout", diff --git a/notebooks/presentations/img/inputoutputflow.pdf b/notebooks/presentations/img/inputoutputflow.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6d46a5603ef25a94a12cc7ece12b6de556c5a66b GIT binary patch literal 14906 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 literal 0 HcmV?d00001 -- GitLab