├── .gitignore ├── PRISM ├── README.md ├── property.props └── threads.prism ├── README.md ├── clean_graph.py ├── gephi ├── 4_threads.gephi └── README.md ├── graphs ├── 1_threads.dot ├── 2_threads.dot ├── 3_threads.dot ├── 4_threads.dot └── 5_threads.dot ├── header.svg ├── likelihoods.py ├── notes.md ├── requirements.txt ├── threads.cfg └── threads.tla /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | venv* 110 | ENV/ 111 | env.bak/ 112 | venv.bak/ 113 | 114 | # Spyder project settings 115 | .spyderproject 116 | .spyproject 117 | 118 | # Rope project settings 119 | .ropeproject 120 | 121 | # mkdocs documentation 122 | /site 123 | 124 | # mypy 125 | .mypy_cache/ 126 | .dmypy.json 127 | dmypy.json 128 | 129 | # Pyre type checker 130 | .pyre/ 131 | 132 | states/ 133 | -------------------------------------------------------------------------------- /PRISM/README.md: -------------------------------------------------------------------------------- 1 | ## Intro 2 | 3 | [PRISM](https://www.prismmodelchecker.org/) is a probabilistic model checker. It can find the probability of reaching a given end state natively, without us needing to dump the entire state graph and run a separate program. It can also output diagrams, show simulations, and more! 4 | 5 | The catch? PRISM doesn't have strings, arrays, functions, or control flow. As such it can't handle interesting industrial problems and is mostly limited to academic research. But I find it neat and included a copy of the thread spec for fun. 6 | 7 | You can download PRISM [here](https://www.prismmodelchecker.org/download.php) and read an introductory tutorial [here](https://www.prismmodelchecker.org/tutorial/). The download asks for a name and organization but it's not actually checked. 8 | 9 | ## Notes 10 | 11 | The only "interesting" thing about the model is that we represent multiple threads with multiple distinct `modules`. When multiple probabilistic actions are enabled, PRISM will pick one at random to happen. We can't tell PRISM to weight one module over another, it'll always be uniform. So instead we have to model the probabilities inside each module's actions with noops (`true`). 12 | 13 | Consider two threads, where `state1=GET` and `state2=INC`. The four possible outcomes are: 14 | 15 | ``` 16 | 0.5*p_get: state1'=INC 17 | 0.5*p_inc: true 18 | 0.5*p_get: true 19 | 0.5*p_inc: state2'=DONE 20 | ``` 21 | 22 | Since the two `true` steps *don't change the state*, they won't chance our analysis of the end-state probabilities. 23 | -------------------------------------------------------------------------------- /PRISM/property.props: -------------------------------------------------------------------------------- 1 | const int final; 2 | 3 | // Probability x ends at `final`, which we choose on verify 4 | S=? [ x=final ] 5 | 6 | // The probability x ends at 4, given x is already 3 7 | P=? [(G F x=4)] / P=? [(F x = 3)] 8 | 9 | -------------------------------------------------------------------------------- /PRISM/threads.prism: -------------------------------------------------------------------------------- 1 | dtmc 2 | formula NumThreads = 4; //update for number of threads 3 | global x : [0..NumThreads]; 4 | const int inc_weight; 5 | 6 | formula p_inc = inc_weight/(1 + inc_weight); 7 | formula p_get = 1/(1 + inc_weight); 8 | const int GET = 1; const int INC = 2; const int DONE = 3; 9 | 10 | module thread1 11 | tmp1 : [0..NumThreads] init 0; 12 | state1: [GET..DONE] init GET; 13 | 14 | [] state1=GET -> p_get: (tmp1' = x) & (state1' = INC) 15 | + p_inc: true; 16 | [] (state1=INC) & (tmp1 < NumThreads) -> p_get : true 17 | + p_inc: (x' = tmp1+1) & (state1' = DONE); 18 | [] state1=DONE -> true; 19 | endmodule 20 | 21 | module thread2 = thread1[tmp1=tmp2, state1=state2] endmodule 22 | module thread3 = thread1[tmp1=tmp3, state1=state3] endmodule 23 | module thread4 = thread1[tmp1=tmp4, state1=state4] endmodule // if you change NumThreads, add/remove modules 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tla-graphing-demo 2 | 3 | ![A graph of a state space](header.svg) 4 | 5 | This is a showcase of using tools to analyse the state graph of a [TLA+](https://learntla.com/) specification. 6 | 7 | The specification models a set of threads nonatomically incrementing a shared counter `x`. The TLA+ model checker can natively check that the end value of `x` is always a specific number or in a specific range, but it can't natively determine how likely you are to end up with each value in that range. To check that, we demonstrate extracting the full state space to a directed graph, then run a program (`likelihood.py`) to calculate the probabilities for us. 8 | 9 | Notes on the program are in `notes.md`. There are also three other extras: 10 | 11 | 1. `clean_graph.py` utility to rename the nodes of a state graph 12 | 2. A [gephi](https://gephi.org/) model of one state space, if you want to do any visualizations 13 | 3. A [PRISM](https://www.prismmodelchecker.org/) probabilistic model of the same system. 14 | 15 | Information on them can be found in their corresponding folders. 16 | 17 | # Use 18 | 19 | ## Generating state spaces 20 | 21 | This is only necessary if you want to try modifying the spec or analyzing a state space with more than 5 threads. 22 | 23 | 1\. Install the [TLA+ tools](https://github.com/tlaplus/tlaplus) 24 | 2\. Adjust the number of threads you want to model check in `threads.cfg`. 25 | 3\. Run 26 | 27 | ```bash 28 | java -jar tlatools.jar threads.tla -dump dot,actionlabels {outfile.dot} 29 | ``` 30 | 31 | This outputs a [graphviz](https://graphviz.org/) file of the whole state space. 32 | 33 | **NOTE:** If you modify the *PlusCal* portion of the spec (everything in the `--algorithm` block), you will have to recompile it to TLA+: 34 | 35 | ```bash 36 | java -cp tla2tools.jar pcal.trans threads.tla 37 | ``` 38 | 39 | ## Analyzing the state space 40 | 41 | 1\. Set up the virtualenv: 42 | 43 | ``` 44 | python -m virtualenv venv-tgd 45 | ./venv-tgd/bin/activate 46 | python -m pip install -R requirements.txt 47 | ``` 48 | 49 | **Warning:** On Windows, `pygraphiz` might not install properly. Manually installing it with `python -m pip install --global-option=build_ext --global-option="-IC:\Program Files\Graphviz\include" --global-option="-LC:\Program Files\Graphviz\lib" pygraphviz` *seems* to work. 50 | 51 | 2\. `python likelihoods.py graph.dot` 52 | 53 | `likelihoods.py` also has the flag `-m` for changing probabilistic weights. 54 | -------------------------------------------------------------------------------- /clean_graph.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from argparse import ArgumentParser 3 | import re 4 | 5 | 6 | def next_id_str(i): 7 | # convert number to string, 1 = a, 2 = b, 26 = z, 27 = aa, 28 = ab, etc. 8 | result = "" 9 | while i > 0: 10 | i = i - 1 11 | result = chr(ord('a') + int((i % 26))) + result 12 | i = i // 26 13 | return result 14 | 15 | def main(file): 16 | i = 0 17 | mapping = {} 18 | graph = Path(file).read_text() 19 | 20 | regex = re.compile(r"-?[0-9]{10,}") 21 | 22 | for match in regex.finditer(graph): 23 | node = match.group() 24 | if not node in mapping.keys(): 25 | i = i + 1 26 | mapping[node] = next_id_str(i) 27 | graph = graph.replace(match.group(), mapping[node]) 28 | print(graph) 29 | 30 | def parse_args(): 31 | parser = ArgumentParser() 32 | parser.add_argument("file", help="graph file to process. Should be a graphviz dotfile") 33 | return parser.parse_args() 34 | 35 | if __name__ == "__main__": 36 | args = parse_args() 37 | main(args.file) 38 | 39 | -------------------------------------------------------------------------------- /gephi/4_threads.gephi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwayne/tla-graphing-demo/46a2206eb58311ee44aaef23db9ef7680e3c480c/gephi/4_threads.gephi -------------------------------------------------------------------------------- /gephi/README.md: -------------------------------------------------------------------------------- 1 | [Gephi](https://gephi.org/) is an interactive graph analysis toolkit I used to make the graph image in the repo. You can try loading the file if you want to play around with it. 2 | 3 | What exists of the documentation can be found [here](https://gephi.org/users/) and [here](https://github.com/gephi/gephi/wiki). It doesn't explain very much and I honestly didn't find any of it helpful. 4 | -------------------------------------------------------------------------------- /graphs/1_threads.dot: -------------------------------------------------------------------------------- 1 | strict digraph DiskGraph { 2 | nodesep=0.35; 3 | subgraph cluster_graph { 4 | color="white"; 5 | 809620043161205007 [label="/\\ x = 0\n/\\ pc = <<\"Get\">>\n/\\ tmp = <<0>>",style = filled] 6 | 809620043161205007 -> -280146466701130858 [label="Get",color="black",fontcolor="black"]; 7 | -280146466701130858 [label="/\\ x = 0\n/\\ pc = <<\"Inc\">>\n/\\ tmp = <<0>>"]; 8 | -280146466701130858 -> -4898893612856533987 [label="Inc",color="black",fontcolor="black"]; 9 | -4898893612856533987 [label="/\\ x = 1\n/\\ pc = <<\"Done\">>\n/\\ tmp = <<0>>"]; 10 | -4898893612856533987 -> -4898893612856533987 [label="Terminating",color="black",fontcolor="black"]; 11 | {rank = same; 809620043161205007;} 12 | {rank = same; -280146466701130858;} 13 | {rank = same; -4898893612856533987;} 14 | } 15 | } -------------------------------------------------------------------------------- /graphs/2_threads.dot: -------------------------------------------------------------------------------- 1 | strict digraph DiskGraph { 2 | nodesep=0.35; 3 | subgraph cluster_graph { 4 | color="white"; 5 | 8830631066581796563 [label="/\\ x = 0\n/\\ pc = <<\"Get\", \"Get\">>\n/\\ tmp = <<0, 0>>",style = filled] 6 | 8830631066581796563 -> -5873426433428885861 [label="Get",color="black",fontcolor="black"]; 7 | -5873426433428885861 [label="/\\ x = 0\n/\\ pc = <<\"Inc\", \"Get\">>\n/\\ tmp = <<0, 0>>"]; 8 | 8830631066581796563 -> 2870009878292733134 [label="Get",color="black",fontcolor="black"]; 9 | 2870009878292733134 [label="/\\ x = 0\n/\\ pc = <<\"Get\", \"Inc\">>\n/\\ tmp = <<0, 0>>"]; 10 | -5873426433428885861 -> -3059675916900725117 [label="Inc",color="black",fontcolor="black"]; 11 | -3059675916900725117 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Get\">>\n/\\ tmp = <<0, 0>>"]; 12 | -5873426433428885861 -> -926189926193040250 [label="Get",color="black",fontcolor="black"]; 13 | -926189926193040250 [label="/\\ x = 0\n/\\ pc = <<\"Inc\", \"Inc\">>\n/\\ tmp = <<0, 0>>"]; 14 | 2870009878292733134 -> -926189926193040250 [label="Get",color="black",fontcolor="black"]; 15 | 2870009878292733134 -> -6775012108110370256 [label="Inc",color="black",fontcolor="black"]; 16 | -6775012108110370256 [label="/\\ x = 1\n/\\ pc = <<\"Get\", \"Done\">>\n/\\ tmp = <<0, 0>>"]; 17 | -3059675916900725117 -> -1087674877596532837 [label="Get",color="black",fontcolor="black"]; 18 | -1087674877596532837 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Inc\">>\n/\\ tmp = <<0, 1>>"]; 19 | -926189926193040250 -> -8588014729825119074 [label="Inc",color="black",fontcolor="black"]; 20 | -8588014729825119074 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Inc\">>\n/\\ tmp = <<0, 0>>"]; 21 | -926189926193040250 -> 4769876239651584632 [label="Inc",color="black",fontcolor="black"]; 22 | 4769876239651584632 [label="/\\ x = 1\n/\\ pc = <<\"Inc\", \"Done\">>\n/\\ tmp = <<0, 0>>"]; 23 | -6775012108110370256 -> 8371755888319824489 [label="Get",color="black",fontcolor="black"]; 24 | 8371755888319824489 [label="/\\ x = 1\n/\\ pc = <<\"Inc\", \"Done\">>\n/\\ tmp = <<1, 0>>"]; 25 | -1087674877596532837 -> 4729350951125352935 [label="Inc",color="black",fontcolor="black"]; 26 | 4729350951125352935 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Done\">>\n/\\ tmp = <<0, 1>>"]; 27 | -8588014729825119074 -> -6396188416730248811 [label="Inc",color="black",fontcolor="black"]; 28 | -6396188416730248811 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Done\">>\n/\\ tmp = <<0, 0>>"]; 29 | 4769876239651584632 -> -6396188416730248811 [label="Inc",color="black",fontcolor="black"]; 30 | 8371755888319824489 -> 1119292367793378035 [label="Inc",color="black",fontcolor="black"]; 31 | 1119292367793378035 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Done\">>\n/\\ tmp = <<1, 0>>"]; 32 | 4729350951125352935 -> 4729350951125352935 [label="Terminating",color="black",fontcolor="black"]; 33 | -6396188416730248811 -> -6396188416730248811 [label="Terminating",color="black",fontcolor="black"]; 34 | 1119292367793378035 -> 1119292367793378035 [label="Terminating",color="black",fontcolor="black"]; 35 | {rank = same; 8830631066581796563;} 36 | {rank = same; -5873426433428885861;2870009878292733134;} 37 | {rank = same; -6775012108110370256;-3059675916900725117;-926189926193040250;} 38 | {rank = same; 4769876239651584632;-8588014729825119074;-1087674877596532837;8371755888319824489;} 39 | {rank = same; 1119292367793378035;4729350951125352935;-6396188416730248811;} 40 | } 41 | } -------------------------------------------------------------------------------- /graphs/3_threads.dot: -------------------------------------------------------------------------------- 1 | strict digraph DiskGraph { 2 | nodesep=0.35; 3 | subgraph cluster_graph { 4 | color="white"; 5 | 8616889038117642335 [label="/\\ x = 0\n/\\ pc = <<\"Get\", \"Get\", \"Get\">>\n/\\ tmp = <<0, 0, 0>>",style = filled] 6 | 8616889038117642335 -> -2213618988058242283 [label="Get",color="black",fontcolor="black"]; 7 | -2213618988058242283 [label="/\\ x = 0\n/\\ pc = <<\"Inc\", \"Get\", \"Get\">>\n/\\ tmp = <<0, 0, 0>>"]; 8 | 8616889038117642335 -> -7353712730852713003 [label="Get",color="black",fontcolor="black"]; 9 | -7353712730852713003 [label="/\\ x = 0\n/\\ pc = <<\"Get\", \"Inc\", \"Get\">>\n/\\ tmp = <<0, 0, 0>>"]; 10 | 8616889038117642335 -> 166523503818359002 [label="Get",color="black",fontcolor="black"]; 11 | 166523503818359002 [label="/\\ x = 0\n/\\ pc = <<\"Get\", \"Get\", \"Inc\">>\n/\\ tmp = <<0, 0, 0>>"]; 12 | -2213618988058242283 -> -451965431526970304 [label="Inc",color="black",fontcolor="black"]; 13 | -451965431526970304 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Get\", \"Get\">>\n/\\ tmp = <<0, 0, 0>>"]; 14 | -2213618988058242283 -> 1090063063120260767 [label="Get",color="black",fontcolor="black"]; 15 | 1090063063120260767 [label="/\\ x = 0\n/\\ pc = <<\"Inc\", \"Inc\", \"Get\">>\n/\\ tmp = <<0, 0, 0>>"]; 16 | -2213618988058242283 -> -7737905913262509168 [label="Get",color="black",fontcolor="black"]; 17 | -7737905913262509168 [label="/\\ x = 0\n/\\ pc = <<\"Inc\", \"Get\", \"Inc\">>\n/\\ tmp = <<0, 0, 0>>"]; 18 | -7353712730852713003 -> 1090063063120260767 [label="Get",color="black",fontcolor="black"]; 19 | -7353712730852713003 -> -6482142090662070411 [label="Inc",color="black",fontcolor="black"]; 20 | -6482142090662070411 [label="/\\ x = 1\n/\\ pc = <<\"Get\", \"Done\", \"Get\">>\n/\\ tmp = <<0, 0, 0>>"]; 21 | -7353712730852713003 -> -1429725858004927152 [label="Get",color="black",fontcolor="black"]; 22 | -1429725858004927152 [label="/\\ x = 0\n/\\ pc = <<\"Get\", \"Inc\", \"Inc\">>\n/\\ tmp = <<0, 0, 0>>"]; 23 | 166523503818359002 -> -7737905913262509168 [label="Get",color="black",fontcolor="black"]; 24 | 166523503818359002 -> -1429725858004927152 [label="Get",color="black",fontcolor="black"]; 25 | 166523503818359002 -> -3000756704093410338 [label="Inc",color="black",fontcolor="black"]; 26 | -3000756704093410338 [label="/\\ x = 1\n/\\ pc = <<\"Get\", \"Get\", \"Done\">>\n/\\ tmp = <<0, 0, 0>>"]; 27 | -451965431526970304 -> 3757236766907421963 [label="Get",color="black",fontcolor="black"]; 28 | 3757236766907421963 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Inc\", \"Get\">>\n/\\ tmp = <<0, 1, 0>>"]; 29 | -451965431526970304 -> 8510376623968379248 [label="Get",color="black",fontcolor="black"]; 30 | 8510376623968379248 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Get\", \"Inc\">>\n/\\ tmp = <<0, 0, 1>>"]; 31 | 1090063063120260767 -> 1719601014415299018 [label="Inc",color="black",fontcolor="black"]; 32 | 1719601014415299018 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Inc\", \"Get\">>\n/\\ tmp = <<0, 0, 0>>"]; 33 | 1090063063120260767 -> 183323579521058148 [label="Inc",color="black",fontcolor="black"]; 34 | 183323579521058148 [label="/\\ x = 1\n/\\ pc = <<\"Inc\", \"Done\", \"Get\">>\n/\\ tmp = <<0, 0, 0>>"]; 35 | 1090063063120260767 -> 8861505473013337626 [label="Get",color="black",fontcolor="black"]; 36 | 8861505473013337626 [label="/\\ x = 0\n/\\ pc = <<\"Inc\", \"Inc\", \"Inc\">>\n/\\ tmp = <<0, 0, 0>>"]; 37 | -7737905913262509168 -> -8331517447039791931 [label="Inc",color="black",fontcolor="black"]; 38 | -8331517447039791931 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Get\", \"Inc\">>\n/\\ tmp = <<0, 0, 0>>"]; 39 | -7737905913262509168 -> 8861505473013337626 [label="Get",color="black",fontcolor="black"]; 40 | -7737905913262509168 -> 8276119004953123279 [label="Inc",color="black",fontcolor="black"]; 41 | 8276119004953123279 [label="/\\ x = 1\n/\\ pc = <<\"Inc\", \"Get\", \"Done\">>\n/\\ tmp = <<0, 0, 0>>"]; 42 | -6482142090662070411 -> -8750900975429164391 [label="Get",color="black",fontcolor="black"]; 43 | -8750900975429164391 [label="/\\ x = 1\n/\\ pc = <<\"Inc\", \"Done\", \"Get\">>\n/\\ tmp = <<1, 0, 0>>"]; 44 | -6482142090662070411 -> 3002330550167075397 [label="Get",color="black",fontcolor="black"]; 45 | 3002330550167075397 [label="/\\ x = 1\n/\\ pc = <<\"Get\", \"Done\", \"Inc\">>\n/\\ tmp = <<0, 0, 1>>"]; 46 | -1429725858004927152 -> 8861505473013337626 [label="Get",color="black",fontcolor="black"]; 47 | -1429725858004927152 -> -3184039647896938512 [label="Inc",color="black",fontcolor="black"]; 48 | -3184039647896938512 [label="/\\ x = 1\n/\\ pc = <<\"Get\", \"Done\", \"Inc\">>\n/\\ tmp = <<0, 0, 0>>"]; 49 | -1429725858004927152 -> -3516503820333543108 [label="Inc",color="black",fontcolor="black"]; 50 | -3516503820333543108 [label="/\\ x = 1\n/\\ pc = <<\"Get\", \"Inc\", \"Done\">>\n/\\ tmp = <<0, 0, 0>>"]; 51 | -3000756704093410338 -> -657692273211490766 [label="Get",color="black",fontcolor="black"]; 52 | -657692273211490766 [label="/\\ x = 1\n/\\ pc = <<\"Inc\", \"Get\", \"Done\">>\n/\\ tmp = <<1, 0, 0>>"]; 53 | -3000756704093410338 -> -1383734808724974083 [label="Get",color="black",fontcolor="black"]; 54 | -1383734808724974083 [label="/\\ x = 1\n/\\ pc = <<\"Get\", \"Inc\", \"Done\">>\n/\\ tmp = <<0, 1, 0>>"]; 55 | 3757236766907421963 -> 6296998201914618885 [label="Inc",color="black",fontcolor="black"]; 56 | 6296998201914618885 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Done\", \"Get\">>\n/\\ tmp = <<0, 1, 0>>"]; 57 | 3757236766907421963 -> -4934600177492846533 [label="Get",color="black",fontcolor="black"]; 58 | -4934600177492846533 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Inc\", \"Inc\">>\n/\\ tmp = <<0, 1, 1>>"]; 59 | 8510376623968379248 -> -4934600177492846533 [label="Get",color="black",fontcolor="black"]; 60 | 8510376623968379248 -> -94075686112378406 [label="Inc",color="black",fontcolor="black"]; 61 | -94075686112378406 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Get\", \"Done\">>\n/\\ tmp = <<0, 0, 1>>"]; 62 | 1719601014415299018 -> 1921197548487705391 [label="Inc",color="black",fontcolor="black"]; 63 | 1921197548487705391 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Done\", \"Get\">>\n/\\ tmp = <<0, 0, 0>>"]; 64 | 1719601014415299018 -> -7458552908056339206 [label="Get",color="black",fontcolor="black"]; 65 | -7458552908056339206 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Inc\", \"Inc\">>\n/\\ tmp = <<0, 0, 1>>"]; 66 | 183323579521058148 -> 1921197548487705391 [label="Inc",color="black",fontcolor="black"]; 67 | 183323579521058148 -> -8274257701010798508 [label="Get",color="black",fontcolor="black"]; 68 | -8274257701010798508 [label="/\\ x = 1\n/\\ pc = <<\"Inc\", \"Done\", \"Inc\">>\n/\\ tmp = <<0, 0, 1>>"]; 69 | 8861505473013337626 -> 7063767169087926607 [label="Inc",color="black",fontcolor="black"]; 70 | 7063767169087926607 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Inc\", \"Inc\">>\n/\\ tmp = <<0, 0, 0>>"]; 71 | 8861505473013337626 -> 8597795003284268513 [label="Inc",color="black",fontcolor="black"]; 72 | 8597795003284268513 [label="/\\ x = 1\n/\\ pc = <<\"Inc\", \"Done\", \"Inc\">>\n/\\ tmp = <<0, 0, 0>>"]; 73 | 8861505473013337626 -> 7760647359390016301 [label="Inc",color="black",fontcolor="black"]; 74 | 7760647359390016301 [label="/\\ x = 1\n/\\ pc = <<\"Inc\", \"Inc\", \"Done\">>\n/\\ tmp = <<0, 0, 0>>"]; 75 | -8331517447039791931 -> 4755423237949666702 [label="Get",color="black",fontcolor="black"]; 76 | 4755423237949666702 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Inc\", \"Inc\">>\n/\\ tmp = <<0, 1, 0>>"]; 77 | -8331517447039791931 -> 7708072717570760580 [label="Inc",color="black",fontcolor="black"]; 78 | 7708072717570760580 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Get\", \"Done\">>\n/\\ tmp = <<0, 0, 0>>"]; 79 | 8276119004953123279 -> 7708072717570760580 [label="Inc",color="black",fontcolor="black"]; 80 | 8276119004953123279 -> 5209109278088898540 [label="Get",color="black",fontcolor="black"]; 81 | 5209109278088898540 [label="/\\ x = 1\n/\\ pc = <<\"Inc\", \"Inc\", \"Done\">>\n/\\ tmp = <<0, 1, 0>>"]; 82 | -8750900975429164391 -> -1108014890231294151 [label="Inc",color="black",fontcolor="black"]; 83 | -1108014890231294151 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Done\", \"Get\">>\n/\\ tmp = <<1, 0, 0>>"]; 84 | -8750900975429164391 -> 661510501709426601 [label="Get",color="black",fontcolor="black"]; 85 | 661510501709426601 [label="/\\ x = 1\n/\\ pc = <<\"Inc\", \"Done\", \"Inc\">>\n/\\ tmp = <<1, 0, 1>>"]; 86 | 3002330550167075397 -> 661510501709426601 [label="Get",color="black",fontcolor="black"]; 87 | 3002330550167075397 -> 840212239068834183 [label="Inc",color="black",fontcolor="black"]; 88 | 840212239068834183 [label="/\\ x = 2\n/\\ pc = <<\"Get\", \"Done\", \"Done\">>\n/\\ tmp = <<0, 0, 1>>"]; 89 | -3184039647896938512 -> -913026489400589796 [label="Get",color="black",fontcolor="black"]; 90 | -913026489400589796 [label="/\\ x = 1\n/\\ pc = <<\"Inc\", \"Done\", \"Inc\">>\n/\\ tmp = <<1, 0, 0>>"]; 91 | -3184039647896938512 -> -6926433486055655463 [label="Inc",color="black",fontcolor="black"]; 92 | -6926433486055655463 [label="/\\ x = 1\n/\\ pc = <<\"Get\", \"Done\", \"Done\">>\n/\\ tmp = <<0, 0, 0>>"]; 93 | -3516503820333543108 -> -1173577370127219504 [label="Get",color="black",fontcolor="black"]; 94 | -1173577370127219504 [label="/\\ x = 1\n/\\ pc = <<\"Inc\", \"Inc\", \"Done\">>\n/\\ tmp = <<1, 0, 0>>"]; 95 | -3516503820333543108 -> -6926433486055655463 [label="Inc",color="black",fontcolor="black"]; 96 | -657692273211490766 -> -9165265175053153390 [label="Inc",color="black",fontcolor="black"]; 97 | -9165265175053153390 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Get\", \"Done\">>\n/\\ tmp = <<1, 0, 0>>"]; 98 | -657692273211490766 -> -3724547860699774959 [label="Get",color="black",fontcolor="black"]; 99 | -3724547860699774959 [label="/\\ x = 1\n/\\ pc = <<\"Inc\", \"Inc\", \"Done\">>\n/\\ tmp = <<1, 1, 0>>"]; 100 | -1383734808724974083 -> -3724547860699774959 [label="Get",color="black",fontcolor="black"]; 101 | -1383734808724974083 -> -3302751456534544141 [label="Inc",color="black",fontcolor="black"]; 102 | -3302751456534544141 [label="/\\ x = 2\n/\\ pc = <<\"Get\", \"Done\", \"Done\">>\n/\\ tmp = <<0, 1, 0>>"]; 103 | 6296998201914618885 -> 8902622503647671623 [label="Get",color="black",fontcolor="black"]; 104 | 8902622503647671623 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Done\", \"Inc\">>\n/\\ tmp = <<0, 1, 2>>"]; 105 | -4934600177492846533 -> -2827192773715904203 [label="Inc",color="black",fontcolor="black"]; 106 | -2827192773715904203 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Done\", \"Inc\">>\n/\\ tmp = <<0, 1, 1>>"]; 107 | -4934600177492846533 -> -4314147720031076359 [label="Inc",color="black",fontcolor="black"]; 108 | -4314147720031076359 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Inc\", \"Done\">>\n/\\ tmp = <<0, 1, 1>>"]; 109 | -94075686112378406 -> -6905576546651983174 [label="Get",color="black",fontcolor="black"]; 110 | -6905576546651983174 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Inc\", \"Done\">>\n/\\ tmp = <<0, 2, 1>>"]; 111 | 1921197548487705391 -> -7707396897332376033 [label="Get",color="black",fontcolor="black"]; 112 | -7707396897332376033 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Done\", \"Inc\">>\n/\\ tmp = <<0, 0, 1>>"]; 113 | -7458552908056339206 -> -7707396897332376033 [label="Inc",color="black",fontcolor="black"]; 114 | -7458552908056339206 -> -1740584715790730440 [label="Inc",color="black",fontcolor="black"]; 115 | -1740584715790730440 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Inc\", \"Done\">>\n/\\ tmp = <<0, 0, 1>>"]; 116 | -8274257701010798508 -> -7707396897332376033 [label="Inc",color="black",fontcolor="black"]; 117 | -8274257701010798508 -> -8551878185796283304 [label="Inc",color="black",fontcolor="black"]; 118 | -8551878185796283304 [label="/\\ x = 2\n/\\ pc = <<\"Inc\", \"Done\", \"Done\">>\n/\\ tmp = <<0, 0, 1>>"]; 119 | 7063767169087926607 -> 8030969467743462314 [label="Inc",color="black",fontcolor="black"]; 120 | 8030969467743462314 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Done\", \"Inc\">>\n/\\ tmp = <<0, 0, 0>>"]; 121 | 7063767169087926607 -> 8327549924903986534 [label="Inc",color="black",fontcolor="black"]; 122 | 8327549924903986534 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Inc\", \"Done\">>\n/\\ tmp = <<0, 0, 0>>"]; 123 | 8597795003284268513 -> 8030969467743462314 [label="Inc",color="black",fontcolor="black"]; 124 | 8597795003284268513 -> 2096696092842871302 [label="Inc",color="black",fontcolor="black"]; 125 | 2096696092842871302 [label="/\\ x = 1\n/\\ pc = <<\"Inc\", \"Done\", \"Done\">>\n/\\ tmp = <<0, 0, 0>>"]; 126 | 7760647359390016301 -> 8327549924903986534 [label="Inc",color="black",fontcolor="black"]; 127 | 7760647359390016301 -> 2096696092842871302 [label="Inc",color="black",fontcolor="black"]; 128 | 4755423237949666702 -> 2502211748300952704 [label="Inc",color="black",fontcolor="black"]; 129 | 2502211748300952704 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Done\", \"Inc\">>\n/\\ tmp = <<0, 1, 0>>"]; 130 | 4755423237949666702 -> 5793956120592015783 [label="Inc",color="black",fontcolor="black"]; 131 | 5793956120592015783 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Inc\", \"Done\">>\n/\\ tmp = <<0, 1, 0>>"]; 132 | 7708072717570760580 -> 5793956120592015783 [label="Get",color="black",fontcolor="black"]; 133 | 5209109278088898540 -> 5793956120592015783 [label="Inc",color="black",fontcolor="black"]; 134 | 5209109278088898540 -> 5823978408250162476 [label="Inc",color="black",fontcolor="black"]; 135 | 5823978408250162476 [label="/\\ x = 2\n/\\ pc = <<\"Inc\", \"Done\", \"Done\">>\n/\\ tmp = <<0, 1, 0>>"]; 136 | -1108014890231294151 -> -2562386370133879173 [label="Get",color="black",fontcolor="black"]; 137 | -2562386370133879173 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Done\", \"Inc\">>\n/\\ tmp = <<1, 0, 2>>"]; 138 | 661510501709426601 -> 9169100097249860105 [label="Inc",color="black",fontcolor="black"]; 139 | 9169100097249860105 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Done\", \"Inc\">>\n/\\ tmp = <<1, 0, 1>>"]; 140 | 661510501709426601 -> 960504941977400229 [label="Inc",color="black",fontcolor="black"]; 141 | 960504941977400229 [label="/\\ x = 2\n/\\ pc = <<\"Inc\", \"Done\", \"Done\">>\n/\\ tmp = <<1, 0, 1>>"]; 142 | 840212239068834183 -> 3214199194000859407 [label="Get",color="black",fontcolor="black"]; 143 | 3214199194000859407 [label="/\\ x = 2\n/\\ pc = <<\"Inc\", \"Done\", \"Done\">>\n/\\ tmp = <<2, 0, 1>>"]; 144 | -913026489400589796 -> -8843591372762581060 [label="Inc",color="black",fontcolor="black"]; 145 | -8843591372762581060 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Done\", \"Inc\">>\n/\\ tmp = <<1, 0, 0>>"]; 146 | -913026489400589796 -> -7413712501025804805 [label="Inc",color="black",fontcolor="black"]; 147 | -7413712501025804805 [label="/\\ x = 1\n/\\ pc = <<\"Inc\", \"Done\", \"Done\">>\n/\\ tmp = <<1, 0, 0>>"]; 148 | -6926433486055655463 -> -7413712501025804805 [label="Get",color="black",fontcolor="black"]; 149 | -1173577370127219504 -> -7374756371660131984 [label="Inc",color="black",fontcolor="black"]; 150 | -7374756371660131984 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Inc\", \"Done\">>\n/\\ tmp = <<1, 0, 0>>"]; 151 | -1173577370127219504 -> -7413712501025804805 [label="Inc",color="black",fontcolor="black"]; 152 | -9165265175053153390 -> -2425786547950470926 [label="Get",color="black",fontcolor="black"]; 153 | -2425786547950470926 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Inc\", \"Done\">>\n/\\ tmp = <<1, 2, 0>>"]; 154 | -3724547860699774959 -> -5017369752875829839 [label="Inc",color="black",fontcolor="black"]; 155 | -5017369752875829839 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Inc\", \"Done\">>\n/\\ tmp = <<1, 1, 0>>"]; 156 | -3724547860699774959 -> -3109968917365712175 [label="Inc",color="black",fontcolor="black"]; 157 | -3109968917365712175 [label="/\\ x = 2\n/\\ pc = <<\"Inc\", \"Done\", \"Done\">>\n/\\ tmp = <<1, 1, 0>>"]; 158 | -3302751456534544141 -> -785758581109798789 [label="Get",color="black",fontcolor="black"]; 159 | -785758581109798789 [label="/\\ x = 2\n/\\ pc = <<\"Inc\", \"Done\", \"Done\">>\n/\\ tmp = <<2, 1, 0>>"]; 160 | 8902622503647671623 -> -6551145710320872111 [label="Inc",color="black",fontcolor="black"]; 161 | -6551145710320872111 [label="/\\ x = 3\n/\\ pc = <<\"Done\", \"Done\", \"Done\">>\n/\\ tmp = <<0, 1, 2>>"]; 162 | -2827192773715904203 -> -1056788130756234414 [label="Inc",color="black",fontcolor="black"]; 163 | -1056788130756234414 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Done\", \"Done\">>\n/\\ tmp = <<0, 1, 1>>"]; 164 | -4314147720031076359 -> -1056788130756234414 [label="Inc",color="black",fontcolor="black"]; 165 | -6905576546651983174 -> 7084580306132112480 [label="Inc",color="black",fontcolor="black"]; 166 | 7084580306132112480 [label="/\\ x = 3\n/\\ pc = <<\"Done\", \"Done\", \"Done\">>\n/\\ tmp = <<0, 2, 1>>"]; 167 | -7707396897332376033 -> -3266048547413215341 [label="Inc",color="black",fontcolor="black"]; 168 | -3266048547413215341 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Done\", \"Done\">>\n/\\ tmp = <<0, 0, 1>>"]; 169 | -1740584715790730440 -> 7385080569573471149 [label="Inc",color="black",fontcolor="black"]; 170 | 7385080569573471149 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Done\", \"Done\">>\n/\\ tmp = <<0, 0, 1>>"]; 171 | -8551878185796283304 -> 7385080569573471149 [label="Inc",color="black",fontcolor="black"]; 172 | 8030969467743462314 -> -7203651831761347048 [label="Inc",color="black",fontcolor="black"]; 173 | -7203651831761347048 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Done\", \"Done\">>\n/\\ tmp = <<0, 0, 0>>"]; 174 | 8327549924903986534 -> -7203651831761347048 [label="Inc",color="black",fontcolor="black"]; 175 | 2096696092842871302 -> -7203651831761347048 [label="Inc",color="black",fontcolor="black"]; 176 | 2502211748300952704 -> -4612145345493198119 [label="Inc",color="black",fontcolor="black"]; 177 | -4612145345493198119 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Done\", \"Done\">>\n/\\ tmp = <<0, 1, 0>>"]; 178 | 5793956120592015783 -> 806117663267479271 [label="Inc",color="black",fontcolor="black"]; 179 | 806117663267479271 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Done\", \"Done\">>\n/\\ tmp = <<0, 1, 0>>"]; 180 | 5823978408250162476 -> -4612145345493198119 [label="Inc",color="black",fontcolor="black"]; 181 | -2562386370133879173 -> 209773057589343853 [label="Inc",color="black",fontcolor="black"]; 182 | 209773057589343853 [label="/\\ x = 3\n/\\ pc = <<\"Done\", \"Done\", \"Done\">>\n/\\ tmp = <<1, 0, 2>>"]; 183 | 9169100097249860105 -> 6244641212199047278 [label="Inc",color="black",fontcolor="black"]; 184 | 6244641212199047278 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Done\", \"Done\">>\n/\\ tmp = <<1, 0, 1>>"]; 185 | 960504941977400229 -> 6244641212199047278 [label="Inc",color="black",fontcolor="black"]; 186 | 3214199194000859407 -> -9193614160370865995 [label="Inc",color="black",fontcolor="black"]; 187 | -9193614160370865995 [label="/\\ x = 3\n/\\ pc = <<\"Done\", \"Done\", \"Done\">>\n/\\ tmp = <<2, 0, 1>>"]; 188 | -8843591372762581060 -> 1730150734083095013 [label="Inc",color="black",fontcolor="black"]; 189 | 1730150734083095013 [label="/\\ x = 1\n/\\ pc = <<\"Done\", \"Done\", \"Done\">>\n/\\ tmp = <<1, 0, 0>>"]; 190 | -7413712501025804805 -> -5993442908190622245 [label="Inc",color="black",fontcolor="black"]; 191 | -5993442908190622245 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Done\", \"Done\">>\n/\\ tmp = <<1, 0, 0>>"]; 192 | -7374756371660131984 -> 1730150734083095013 [label="Inc",color="black",fontcolor="black"]; 193 | -2425786547950470926 -> 2030794728606015016 [label="Inc",color="black",fontcolor="black"]; 194 | 2030794728606015016 [label="/\\ x = 3\n/\\ pc = <<\"Done\", \"Done\", \"Done\">>\n/\\ tmp = <<1, 2, 0>>"]; 195 | -5017369752875829839 -> -8130649342741874406 [label="Inc",color="black",fontcolor="black"]; 196 | -8130649342741874406 [label="/\\ x = 2\n/\\ pc = <<\"Done\", \"Done\", \"Done\">>\n/\\ tmp = <<1, 1, 0>>"]; 197 | -3109968917365712175 -> -8130649342741874406 [label="Inc",color="black",fontcolor="black"]; 198 | -785758581109798789 -> 6479260640957366721 [label="Inc",color="black",fontcolor="black"]; 199 | 6479260640957366721 [label="/\\ x = 3\n/\\ pc = <<\"Done\", \"Done\", \"Done\">>\n/\\ tmp = <<2, 1, 0>>"]; 200 | -6551145710320872111 -> -6551145710320872111 [label="Terminating",color="black",fontcolor="black"]; 201 | -1056788130756234414 -> -1056788130756234414 [label="Terminating",color="black",fontcolor="black"]; 202 | 7084580306132112480 -> 7084580306132112480 [label="Terminating",color="black",fontcolor="black"]; 203 | -3266048547413215341 -> -3266048547413215341 [label="Terminating",color="black",fontcolor="black"]; 204 | 7385080569573471149 -> 7385080569573471149 [label="Terminating",color="black",fontcolor="black"]; 205 | -7203651831761347048 -> -7203651831761347048 [label="Terminating",color="black",fontcolor="black"]; 206 | -4612145345493198119 -> -4612145345493198119 [label="Terminating",color="black",fontcolor="black"]; 207 | 806117663267479271 -> 806117663267479271 [label="Terminating",color="black",fontcolor="black"]; 208 | 209773057589343853 -> 209773057589343853 [label="Terminating",color="black",fontcolor="black"]; 209 | 6244641212199047278 -> 6244641212199047278 [label="Terminating",color="black",fontcolor="black"]; 210 | -9193614160370865995 -> -9193614160370865995 [label="Terminating",color="black",fontcolor="black"]; 211 | 1730150734083095013 -> 1730150734083095013 [label="Terminating",color="black",fontcolor="black"]; 212 | -5993442908190622245 -> -5993442908190622245 [label="Terminating",color="black",fontcolor="black"]; 213 | 2030794728606015016 -> 2030794728606015016 [label="Terminating",color="black",fontcolor="black"]; 214 | -8130649342741874406 -> -8130649342741874406 [label="Terminating",color="black",fontcolor="black"]; 215 | 6479260640957366721 -> 6479260640957366721 [label="Terminating",color="black",fontcolor="black"]; 216 | {rank = same; 8616889038117642335;} 217 | {rank = same; 166523503818359002;-2213618988058242283;-7353712730852713003;} 218 | {rank = same; 1090063063120260767;-6482142090662070411;-451965431526970304;-7737905913262509168;-3000756704093410338;-1429725858004927152;} 219 | {rank = same; 8276119004953123279;8861505473013337626;-657692273211490766;-1383734808724974083;3757236766907421963;-3516503820333543108;183323579521058148;1719601014415299018;8510376623968379248;-8750900975429164391;3002330550167075397;-3184039647896938512;-8331517447039791931;} 220 | {rank = same; -6926433486055655463;6296998201914618885;-4934600177492846533;5209109278088898540;-3724547860699774959;-94075686112378406;-9165265175053153390;1921197548487705391;4755423237949666702;-3302751456534544141;8597795003284268513;7063767169087926607;-913026489400589796;661510501709426601;840212239068834183;-7458552908056339206;7760647359390016301;-8274257701010798508;-1108014890231294151;-1173577370127219504;7708072717570760580;} 221 | {rank = same; -7707396897332376033;-8551878185796283304;2502211748300952704;-785758581109798789;8902622503647671623;-4314147720031076359;8327549924903986534;-2425786547950470926;960504941977400229;-2827192773715904203;8030969467743462314;-6905576546651983174;-5017369752875829839;9169100097249860105;-3109968917365712175;3214199194000859407;5823978408250162476;-7374756371660131984;-2562386370133879173;2096696092842871302;-1740584715790730440;5793956120592015783;-8843591372762581060;-7413712501025804805;} 222 | {rank = same; -1056788130756234414;2030794728606015016;7385080569573471149;-7203651831761347048;7084580306132112480;-9193614160370865995;-6551145710320872111;-8130649342741874406;1730150734083095013;806117663267479271;-4612145345493198119;-5993442908190622245;209773057589343853;-3266048547413215341;6244641212199047278;6479260640957366721;} 223 | } 224 | } -------------------------------------------------------------------------------- /likelihoods.py: -------------------------------------------------------------------------------- 1 | """Anything with l:string has a longer explanation in notes.md.""" 2 | 3 | import networkx as nx # l:nx_reference 4 | import re 5 | from argparse import ArgumentParser 6 | from collections import Counter 7 | 8 | def make_graph(path) -> nx.DiGraph: 9 | G = nx.nx_agraph.read_dot(path) # l:read_dot 10 | G.remove_edges_from(nx.selfloop_edges(G)) #l:remove_edges 11 | for node in G: 12 | label = G.nodes[node]["label"] 13 | x = re.search(r'x\s*=\s*(\d+)', label).group(1) # pyright: ignore # l:re 14 | G.nodes[node]["x"] = int(x) 15 | G.root = [n for n, deg in G.in_degree() if deg == 0][0] # l:root 16 | return G 17 | 18 | def weight_edges(G: nx.DiGraph, inc_weight=1.0): # l:weight_edges 19 | """Assigns a probability weight to the every outbound edge of a node. 20 | 21 | inc_weight is relative weight of increment transitions normalized to a `get` transition.""" 22 | 23 | for n in G: 24 | total = 0 25 | edges = [] # l:we:alias 26 | for edge in G.out_edges(n): 27 | data_alias = G.get_edge_data(*edge) # l:we:alias 28 | if data_alias['label'] == "Inc": # type: ignore 29 | v = inc_weight 30 | else: 31 | v = 1.0 32 | total += v 33 | edges.append((data_alias, v)) 34 | for alias, weight in edges: 35 | alias['weight'] = weight / total # l:we:cache 36 | if edges: 37 | assert abs(sum([alias["weight"] for alias, _ in edges]) - 1) < 1e-2 # l:we:assert 38 | 39 | def set_likelihoods(G): 40 | nx.set_node_attributes(G, None, "likelihood") # l:set_likelihood 41 | for n in G: 42 | G.nodes[n]["likelihood"] = get_likelihood(G, n) 43 | 44 | def get_likelihood(G, n): # l:get_likelihood 45 | if n == G.root: # root node l:root 46 | return 1 47 | if l := G.nodes[n].get("likelihood"): 48 | return l 49 | 50 | likelihood = 0 51 | for source in G.predecessors(n): 52 | likelihood += get_likelihood(G, source)*G[source][n]['weight'] # l:gl:recursion 53 | return likelihood 54 | 55 | 56 | def main(path=r"./graphs/2_threads.dot", weight=1): 57 | G = make_graph(path) 58 | weight_edges(G, weight) 59 | set_likelihoods(G) 60 | terminals = [n for n, deg in G.out_degree() if deg == 0] # l:terminals 61 | out = Counter() 62 | for t in terminals: 63 | out[G.nodes[t]["x"]] += get_likelihood(G, t) 64 | print(out) 65 | 66 | def parse_args(): # l:parse_args 67 | parser = ArgumentParser() 68 | parser.add_argument("file", help="graph file to process. Should be a graphviz dotfile") 69 | parser.add_argument("-w", "--weight", type=int, default=1, help="print output STDOUT instead of modifying originally file.") 70 | # TODO format flag 71 | return parser.parse_args() 72 | 73 | if __name__ == "__main__": 74 | args = parse_args() 75 | out = main(args.file, args.weight) 76 | -------------------------------------------------------------------------------- /notes.md: -------------------------------------------------------------------------------- 1 | ## Likelihoods 2 | 3 | ### Overview 4 | 5 | Todo. 6 | 7 | ### l:nx\_reference 8 | 9 | [NetworkX](https://networkx.org/documentation/stable/reference/introduction.html) is a graph analysis library for Python. The graph is represented by a dict of dict of dicts: `G[n]` generates all nodes reachable from `n`, `G[n][p]` is the edge between `n` and `p`, and the edge itself is a dictionary of the edge's attributes. You get attributes of the node itself with `G.nodes[n]`. 10 | 11 |
Used methods 12 | 13 | * [DiGraph](https://networkx.org/documentation/stable/reference/classes/digraph.html) 14 | * [selfloop_edges](https://networkx.org/documentation/stable/reference/generated/networkx.classes.function.selfloop_edges.html) 15 | * [out_edges](https://networkx.org/documentation/stable/reference/classes/generated/networkx.DiGraph.out_edges.html#networkx.DiGraph.out_edges) 16 | * [read_dot](https://networkx.org/documentation/stable/reference/generated/networkx.drawing.nx_agraph.read_dot.html#networkx.drawing.nx_agraph.read_dot) 17 | * [get_edge_data](https://networkx.org/documentation/stable/reference/classes/generated/networkx.DiGraph.get_edge_data.html#networkx.DiGraph.get_edge_data) 18 | * [remove_edges_from](https://networkx.org/documentation/stable/reference/classes/generated/networkx.DiGraph.remove_edges_from.html#networkx.DiGraph.remove_edges_from) 19 | * [in_degree](https://networkx.org/documentation/stable/reference/classes/generated/networkx.DiGraph.in_degree.html#digraph-in-degree) 20 | * [out_degree](https://networkx.org/documentation/stable/reference/classes/generated/networkx.DiGraph.out_degree.html#networkx.DiGraph.out_degree) 21 | * [nodes](https://networkx.org/documentation/stable/reference/classes/generated/networkx.DiGraph.nodes.html#networkx.DiGraph.nodes) 22 | * [edges](https://networkx.org/documentation/stable/reference/classes/generated/networkx.DiGraph.edges.html#networkx.DiGraph.edges) 23 | * [predecessors](https://networkx.org/documentation/stable/reference/classes/generated/networkx.DiGraph.predecessors.html#networkx.DiGraph.predecessors) 24 | * [set_node_attributes](https://networkx.org/documentation/stable/reference/generated/networkx.classes.function.set_node_attributes.html#set-node-attributes) 25 | 26 |
27 | 28 | ### l:read\_dot (7) 29 | 30 | This is the performance bottleneck of the script, probably something like 90% of the time is spent here. If you want to try doing multiple analyses of the same graph, you can read the dotfile just once and use [DiGraph.copy](https://networkx.org/documentation/stable/reference/classes/generated/networkx.DiGraph.copy.html#networkx.DiGraph.copy) to make new copies. 31 | 32 | ### l:remove\_edges (8) 33 | 34 | The TLA+ spec has self-loops from each `Done` state to itself, in order to prevent the algorithm completion counting as a [deadlock](https://learntla.com/core/concurrency.html#index-5). But those make analyzing the graph annoying so we remove them. 35 | 36 | ### l:label (11, 21) 37 | 38 | The TLA+ dump natively uses the node label to include all of the state variables and the edge label to store the action name: 39 | 40 | ```gv 41 | 2870009878292733134 [label="/\\ x = 0\n/\\ pc = <<\"Get\", \"Inc\">>\n/\\ tmp = <<0, 0>>"]; 42 | 8830631066581796563 -> 2870009878292733134 [label="Get",color="black",fontcolor="black"]; 43 | ``` 44 | 45 | The only way to extract the label is with a regex. 46 | 47 | ### l:root (13, 43) 48 | 49 | Each graph has only one starting state so only one root. I attach it to the graph out of pure convenience so I can later use it in `get_likelihood` (13). I have *no idea* why pyright doesn't complain about this. 50 | 51 | ### l:weight\_edges (16) 52 | 53 | The probability of each edge being taken from a given state. Not all transitions are equally likely, as some represent a thread getting interrupted, which is comparatively less likely. Computing this is *real annoying* though, so instead we assume that the history doesn't matter. Given the *subgraph* 54 | 55 | ``` 56 | Z 57 | | 58 | [inc] 59 | v 60 | Y -[get]> A -[inc]> B 61 | | 62 | [get] 63 | v 64 | C 65 | ``` 66 | 67 | The probabilities of `A -> B` and `A -> C` are independent of if we got to A via Z or if we got there via Y. Instead, we roughly mimic the path dependence by saying that `inc` transitions are more likely than `get` transitions. The parameter `inc_weight` is how much more likely an `inc` transition is. 68 | 69 | We define the probability of a given transition `T` as the total weight of all outbound transitions divided by the local weight of `T`. So if `inc_weight=1`, then the weight of `A -[inc]> B` and `A -[get]> c` are both `0.5`. If `inc_weight=2`, it's instead `2/3` and `1/3`. Note the sum should always be 1. 70 | 71 | If you want to make a `get` *more* likely than an `inc`, just choose `inc_weight < 1`. 72 | 73 | 74 | #### l:we:alias (27, 33) 75 | 76 | This is my favorite little bit of the code. `G.out_edges` returns an *iterator* over the outbound edges, so is consumed in the for loop. We only know the total weight after iterating through all of them. By storing each alias after iterating through it, we can save an extra call to `G.out_edges`. 77 | 78 | Honestly it probably doesn't save *that* much time. I just like using dangerous features in scripts. Hurrah for aliasing! 79 | 80 | #### l:we:cache (33) 81 | 82 | Calculating the weight is expensive and we might need to look up the weight a whole bunch of times so might as well add it to the edge data. 83 | 84 | #### l:we:assert (34) 85 | 86 | Doesn't always add up to exactly 1 due to floating point errors, so we only check it's within 0.02. We need the `if edges:` conditional because this check only applies if there are outbound edges, ie `n` isn't a terminal state. Ideally I'd be able to write this as `assert edges => blah blah blah` but programming languages never seem to have an implication operator :( 87 | 88 | ### l:set\_likelihood (38) 89 | 90 | Since we're caching the likelihoods in the node attributes, we need to blow them all away if we want to recalculate the likelihoods, say with a different `inc_weight`. 91 | 92 | We don't actually recalculate the likelihoods in the script, so the line isn't strictly necessary. I added it in case you want to play around with the graphs some more. 93 | 94 | ### l:get\_likelihood (42) 95 | 96 | The likelihood of reaching a state is the sum of the likelihoods of prior states, times the probability of the edges from it. Let's look at a diagram again: 97 | 98 | ``` 99 | (0.5) B -[get]> D 100 | | 101 | [get] 102 | v 103 | A -[get]> C 104 | (0.25) 105 | ``` 106 | 107 | B has a likelihood of 50%, A of 25%. B has a 50% chance of transitioning to C and a 50% chance of transitioning to D, A has a 100% chance of transitioning to C. So the likelihood of C is `0.5*0.5 + 0.25 * 1 = 0.5`. 108 | 109 | This only works because the state graphs are acyclic. If we had cycles we'd need to model this as a [stochastic matrix](https://en.wikipedia.org/wiki/Stochastic_matrix) and that's not fun. 110 | 111 | 112 | #### l:gl:recursion (42) 113 | 114 | This potentially does unnecessary work when no nodes have assigned likelihoods. First, if a node has multiple predecessors, you'll have redundant calculations. 115 | 116 | ``` 117 | A -> B -> D 118 | | ^ 119 | v | 120 | C --------+ 121 | ``` 122 | 123 | `get_likelihood(_, D)` calls `gl(_, B)` and `gl(_, C)`, which *both* call `gl(_, A)`. This means we're calculating the likelihood of `A` twice. If `B` and `C` already have assigned likelihoods, then this problem goes away. 124 | 125 | You still have inefficiencies in computing the whole graph, though. If `set_likelihoods` iterates `A, B, C, D`, then we compute A once, B once, C once, D once. If instead it iterates `D, C, B, A`, we compute D once, C twice, B twice, and A four times. Right now it's not a big slowdown compared to the overhead of importing a graphviz file, but I can imagine it'd add up for really big graphs. The solution would be to [topologically sort](https://docs.python.org/3/library/graphlib.html) the graph. 126 | 127 | ### l:terminals (58) 128 | 129 | The terminal states of the graph are the ones with no outbound edges. Since every behavior ends in a terminal state, their likelihoods sum up to 1. Note we had to remove the self loops in `make_graph` to properly find them. 130 | 131 | While pyright didn't complain about `G.root`, it does complain if I try to do the same with `G.terminals`. Fortunately I only need it here so no biggie. 132 | 133 | ### l:parse\_args (TODO) 134 | 135 | This is a pattern I use in a lot of my scripts: 136 | 137 | ```py 138 | def main(args): 139 | # actual code goes here 140 | 141 | def parse_args(): # l:parse_args 142 | parser = ArgumentParser() 143 | # add a bunch of arguments 144 | parser.add_argument("arg1") 145 | return parser.parse_args() 146 | 147 | if __name__ == "__main__": 148 | args = parse_args() 149 | out = main(args) 150 | ``` 151 | 152 | I like this because it's easier to import into other python files and the poke at in the REPL. 153 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | networkx==3.0 2 | pygraphviz==1.10 3 | -------------------------------------------------------------------------------- /threads.cfg: -------------------------------------------------------------------------------- 1 | SPECIFICATION Spec 2 | CONSTANTS 3 | NumThreads = 2 \* Change this to w/e 4 | -------------------------------------------------------------------------------- /threads.tla: -------------------------------------------------------------------------------- 1 | ------------------------------ MODULE threads ------------------------------ 2 | EXTENDS Integers 3 | CONSTANT NumThreads 4 | Threads == 1..NumThreads \* Set of {1, 2, ... NumThreads} 5 | 6 | \* Anything inside the `(* --algorithm` is "PlusCal" 7 | (* --algorithm threads 8 | 9 | variables 10 | x = 0; \* global 11 | 12 | process thread \in Threads \* One thread process per element of Threads 13 | variables tmp = 0; \* tmp is local to each process 14 | begin 15 | Get: \* "Label". Each process Gets then Incs, but other threads can interrupt 16 | tmp := x; 17 | 18 | Inc: 19 | x := tmp + 1; 20 | end process; 21 | 22 | end algorithm; *) 23 | 24 | \* Everything in BEGIN TRANSLATION is automatically generated from the pluscal 25 | \* BEGIN TRANSLATION - the hash of the PCal code: PCal-2cfd730c7e541cfac6a18f77d5288391 26 | VARIABLES x, pc, tmp 27 | 28 | vars == << x, pc, tmp >> 29 | 30 | ProcSet == (Threads) 31 | 32 | Init == (* Global variables *) 33 | /\ x = 0 34 | (* Process thread *) 35 | /\ tmp = [self \in Threads |-> 0] 36 | /\ pc = [self \in ProcSet |-> "Get"] 37 | 38 | Get(self) == /\ pc[self] = "Get" 39 | /\ tmp' = [tmp EXCEPT ![self] = x] 40 | /\ pc' = [pc EXCEPT ![self] = "Inc"] 41 | /\ x' = x 42 | 43 | Inc(self) == /\ pc[self] = "Inc" 44 | /\ x' = tmp[self] + 1 45 | /\ pc' = [pc EXCEPT ![self] = "Done"] 46 | /\ tmp' = tmp 47 | 48 | thread(self) == Get(self) \/ Inc(self) 49 | 50 | (* Allow infinite stuttering to prevent deadlock on termination. *) 51 | Terminating == /\ \A self \in ProcSet: pc[self] = "Done" 52 | /\ UNCHANGED vars 53 | 54 | Next == (\E self \in Threads: thread(self)) 55 | \/ Terminating 56 | 57 | Spec == Init /\ [][Next]_vars 58 | 59 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 60 | 61 | \* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-3b1540a4627b3d3586cb172c60eb0c0c 62 | 63 | \* Normally there'd be invariants and properties described here, but since we're just 64 | \* using this to generate the state graph, we don't need any 65 | 66 | ==== 67 | 68 | --------------------------------------------------------------------------------