Skip to content

Commit 443d2be

Browse files
committed
Update README.md
1 parent 3110500 commit 443d2be

1 file changed

Lines changed: 86 additions & 85 deletions

File tree

README.md

Lines changed: 86 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,9 @@ Now let's run this simulation for `10` units of parallel time (`10 * n` interact
6868
sim.run(10, 0.1)
6969
```
7070

71-
Time: 10.000
72-
71+
```
72+
Time: 10.000
73+
```
7374

7475
The `Simulation` class can display all these configurations in a `pandas` dataframe in the attribute `history`.
7576

@@ -178,7 +179,7 @@ p = sim.history.plot()
178179

179180

180181

181-
![png](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/README_12_0.png)
182+
![png](README_files/README_12_0.png)
182183

183184

184185

@@ -189,13 +190,13 @@ Without specifying an end time, `run` will run the simulation until the configur
189190
sim.run()
190191
p = sim.history.plot()
191192
```
192-
193-
Time: 21.000
194-
193+
```
194+
Time: 21.000
195+
```
195196

196197

197198

198-
![png](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/README_14_1.png)
199+
![png](README_files/README_14_1.png)
199200

200201

201202

@@ -220,16 +221,16 @@ print(sim.reactions)
220221
sim.run()
221222
p = sim.history.plot()
222223
```
223-
224-
A, B --> U, U with probability 0.5
225-
A, U --> A, A with probability 0.5
226-
B, U --> B, B with probability 0.5
227-
Time: 44.000
228-
224+
```
225+
A, B --> U, U with probability 0.5
226+
A, U --> A, A with probability 0.5
227+
B, U --> B, B with probability 0.5
228+
Time: 44.000
229+
```
229230

230231

231232

232-
![png](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/README_18_1.png)
233+
![png](README_files/README_18_1.png)
233234

234235

235236

@@ -332,7 +333,7 @@ lp.set_xscale('log')
332333

333334

334335

335-
![png](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/README_22_0.png)
336+
![png](README_files/README_22_0.png)
336337

337338

338339

@@ -360,9 +361,9 @@ We did not need to explicitly describe the state set. Upon initialization, `Simu
360361
```python
361362
print(sim.state_list)
362363
```
363-
364-
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
365-
364+
```
365+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
366+
```
366367

367368
This enumeration will call the function `rule` we give it O(q^2) times, where q is the number of reachable states. This preprocessing step also builds an internal representation of the transition function, so it will not need to continue calling `rule`. Thus we don't need to worry too much about our code for `rule` being efficient.
368369

@@ -375,9 +376,9 @@ sim.config_dict
375376

376377

377378

378-
379-
{0: 500000, 50: 500000}
380-
379+
```
380+
{0: 500000, 50: 500000}
381+
```
381382

382383

383384

@@ -387,15 +388,15 @@ sim.config_array
387388

388389

389390

390-
391-
array([500000, 0, 0, 0, 0, 0, 0, 0,
392-
0, 0, 0, 0, 0, 0, 0, 0,
393-
0, 0, 0, 0, 0, 0, 0, 0,
394-
0, 0, 0, 0, 0, 0, 0, 0,
395-
0, 0, 0, 0, 0, 0, 0, 0,
396-
0, 0, 0, 0, 0, 0, 0, 0,
397-
0, 0, 500000], dtype=int64)
398-
391+
```
392+
array([500000, 0, 0, 0, 0, 0, 0, 0,
393+
0, 0, 0, 0, 0, 0, 0, 0,
394+
0, 0, 0, 0, 0, 0, 0, 0,
395+
0, 0, 0, 0, 0, 0, 0, 0,
396+
0, 0, 0, 0, 0, 0, 0, 0,
397+
0, 0, 0, 0, 0, 0, 0, 0,
398+
0, 0, 500000], dtype=int64)
399+
```
399400

400401

401402
A key result about these discrete averaging dynamics is that they converge in O(log n) time to at most 3 consecutive values. It could take longer to reach the ultimate silent configuration with only 2 consecutive values, so if we wanted to check for the faster convergence condition, we could use a function that checks for the condition. This function takes a configuration dictionary (mapping states to counts) as input and returns `True` if the convergence criterion has been met.
@@ -763,19 +764,19 @@ plot_row(-1)
763764

764765

765766

766-
![png](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/README_35_0.png)
767+
![png](README_files/README_35_0.png)
767768

768769

769770

770771

771772

772-
![png](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/README_35_1.png)
773+
![png](README_files/README_35_1.png)
773774

774775

775776

776777

777778

778-
![png](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/README_35_2.png)
779+
![png](README_files/README_35_2.png)
779780

780781

781782

@@ -788,7 +789,7 @@ bar = widgets.interact(plot_row, row = widgets.IntSlider(
788789
min=0, max=len(sim.history)-1, step=1, value=0, layout = widgets.Layout(width='100%')))
789790
```
790791

791-
![gif](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/barplot1.gif)
792+
![gif](README_files/barplot1.gif)
792793

793794
It is recommended to use an interactive matplotlib backend, such as `ipympl`, which can be installed with `pip install ipympl` and then activated with the cell magic `%matplotlib widget`. The recommended environment to use for these notebooks is [Jupyter Lab](https://jupyterlab.readthedocs.io/en/stable/). Unfortunately, these interactive backends are not supported with [Google Colab](https://colab.research.google.com/), so there does not seem to be an easy way to have access to interactive backends with something that can be run only in a browser without local installation.
794795

@@ -851,15 +852,15 @@ bar = widgets.interact(plot_row,
851852
yscale = ['linear','symlog'])
852853
```
853854

854-
![gif](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/barplot2.gif)
855+
![gif](README_files/barplot2.gif)
855856

856857
## Protocol with Multiple Fields
857858

858859
For more complicated protocol, it is helpful to have the states be more complicated Python objects. A recommended method is to define an Agent [dataclass](https://docs.python.org/3/library/dataclasses.html) that includes various fields.
859860

860861
As a concrete example, we will use the protocol from [Simple and Efficient Leader Election](https://drops.dagstuhl.de/opus/volltexte/2018/8302/pdf/OASIcs-SOSA-2018-9.pdf). We start by translating the explicit description of an agents state into our Agent class.
861862

862-
![png](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/SimpleLeaderElection1.PNG)
863+
![png](README_files/SimpleLeaderElection1.PNG)
863864

864865

865866
```python
@@ -879,7 +880,7 @@ class Agent:
879880
counter: int = 0
880881
```
881882

882-
![png](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/SimpleLeaderElection2.PNG)
883+
![png](README_files/SimpleLeaderElection2.PNG)
883884

884885

885886
```python
@@ -911,7 +912,7 @@ def leader_election(v: Agent, u: Agent, loglogn: int, Ulogn: int):
911912

912913
The pseudocode was described in the following way:
913914

914-
![png](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/SimpleLeaderElection3.PNG)
915+
![png](README_files/SimpleLeaderElection3.PNG)
915916

916917
We can implement this assumption by having our transition rule call the the `leader_election` function twice:
917918

@@ -940,7 +941,7 @@ plt.show()
940941

941942

942943

943-
![png](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/README_53_0.png)
944+
![png](README_files/README_53_0.png)
944945

945946

946947

@@ -1327,30 +1328,30 @@ sim.history.columns
13271328

13281329

13291330

1330-
1331-
MultiIndex([('contender', 0, 0, 'marking', 0),
1332-
('contender', 0, 0, 'marking', 2),
1333-
('contender', 0, 0, 'marking', 4),
1334-
('contender', 0, 0, 'marking', 6),
1335-
('contender', 0, 0, 'marking', 8),
1336-
('contender', 0, 0, 'marking', 10),
1337-
('contender', 0, 0, 'marking', 12),
1338-
('contender', 0, 0, 'marking', 14),
1339-
('contender', 0, 0, 'tournament', 12),
1340-
('contender', 0, 0, 'tournament', 13),
1341-
...
1342-
( 'minion', 1, 1, 'tournament', 50),
1343-
( 'minion', 1, 1, 'tournament', 51),
1344-
( 'minion', 1, 1, 'tournament', 52),
1345-
( 'minion', 1, 1, 'tournament', 53),
1346-
( 'minion', 1, 1, 'tournament', 54),
1347-
( 'minion', 1, 1, 'tournament', 55),
1348-
( 'minion', 1, 1, 'tournament', 56),
1349-
( 'minion', 1, 1, 'tournament', 57),
1350-
( 'minion', 1, 1, 'tournament', 58),
1351-
( 'minion', 1, 1, 'tournament', 59)],
1352-
names=['role', 'flip_bit', 'marker', 'phase', 'counter'], length=384)
1353-
1331+
```
1332+
MultiIndex([('contender', 0, 0, 'marking', 0),
1333+
('contender', 0, 0, 'marking', 2),
1334+
('contender', 0, 0, 'marking', 4),
1335+
('contender', 0, 0, 'marking', 6),
1336+
('contender', 0, 0, 'marking', 8),
1337+
('contender', 0, 0, 'marking', 10),
1338+
('contender', 0, 0, 'marking', 12),
1339+
('contender', 0, 0, 'marking', 14),
1340+
('contender', 0, 0, 'tournament', 12),
1341+
('contender', 0, 0, 'tournament', 13),
1342+
...
1343+
( 'minion', 1, 1, 'tournament', 50),
1344+
( 'minion', 1, 1, 'tournament', 51),
1345+
( 'minion', 1, 1, 'tournament', 52),
1346+
( 'minion', 1, 1, 'tournament', 53),
1347+
( 'minion', 1, 1, 'tournament', 54),
1348+
( 'minion', 1, 1, 'tournament', 55),
1349+
( 'minion', 1, 1, 'tournament', 56),
1350+
( 'minion', 1, 1, 'tournament', 57),
1351+
( 'minion', 1, 1, 'tournament', 58),
1352+
( 'minion', 1, 1, 'tournament', 59)],
1353+
names=['role', 'flip_bit', 'marker', 'phase', 'counter'], length=384)
1354+
```
13541355

13551356

13561357
We can use the pandas [groupby](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.groupby.html) function to conveniently look at the values of just one field. For a field whose name is the string `field`, then calling `sim.history.groupby(field, axis=1).sum()` gives the counts of values of just a single state. If we have a set of fields `field1, field2, ...` then calling `sim.history.groupby([field1, field2, ...], axis=1).sum()` will give the counts of values of just those fields.
@@ -1452,7 +1453,7 @@ plt.show()
14521453

14531454

14541455

1455-
![png](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/README_63_0.png)
1456+
![png](README_files/README_63_0.png)
14561457

14571458

14581459

@@ -1464,7 +1465,7 @@ plt.show()
14641465

14651466

14661467

1467-
![png](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/README_64_0.png)
1468+
![png](README_files/README_64_0.png)
14681469

14691470

14701471

@@ -1480,21 +1481,21 @@ df.iloc[10]
14801481

14811482

14821483

1483-
1484-
counter role
1485-
0 contender 2
1486-
1 contender 48
1487-
2 contender 441
1488-
3 contender 2876
1489-
4 contender 13600
1490-
...
1491-
57 minion 0
1492-
58 contender 0
1493-
minion 0
1494-
59 contender 0
1495-
minion 0
1496-
Name: 10.0, Length: 108, dtype: int64
1497-
1484+
```
1485+
counter role
1486+
0 contender 2
1487+
1 contender 48
1488+
2 contender 441
1489+
3 contender 2876
1490+
4 contender 13600
1491+
...
1492+
57 minion 0
1493+
58 contender 0
1494+
minion 0
1495+
59 contender 0
1496+
minion 0
1497+
Name: 10.0, Length: 108, dtype: int64
1498+
```
14981499

14991500

15001501
Then calling `unstack()` on the series will give pull off the first field, and give us a dataframe that can immediately plotted as a multibar plot.
@@ -1836,7 +1837,7 @@ plt.show()
18361837

18371838

18381839

1839-
![png](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/README_69_0.png)
1840+
![png](README_files/README_69_0.png)
18401841

18411842

18421843

@@ -1855,14 +1856,14 @@ bar = widgets.interact(plot_row,
18551856
yscale = ['linear','symlog'])
18561857
```
18571858

1858-
![gif](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/barplot3.gif)
1859+
![gif](README_files/barplot3.gif)
18591860

18601861

18611862
## Simulating Chemical Reaction Networks (CRNs)
18621863

18631864
`ppsim` is able to simulate any Chemical Reaction Network that has only bimolecular (2-input, 2-output) and unimolecular (1-input, 1-output) reactions. There is a special syntax used to specify CRNs, such as
18641865

1865-
![png](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/CRN.PNG)
1866+
![png](README_files/CRN.PNG)
18661867

18671868

18681869

@@ -1886,7 +1887,7 @@ p = sim.history.plot()
18861887

18871888

18881889

1889-
![png](https://github.com/UC-Davis-molecular-computing/ppsim/blob/main/README_files/README_75_1.png)
1890+
![png](README_files/README_75_1.png)
18901891

18911892

18921893

@@ -1898,4 +1899,4 @@ By default, `ppsim` sets the volume `v` to be the population size `n`, which mak
18981899
For more details about the CRN model and how it is faithfully represented as a continuous time population protocol, see [this paper](https://arxiv.org/abs/2105.04702).
18991900

19001901
## More examples
1901-
See https://github.com/UC-Davis-molecular-computing/population-protocols-python-package/tree/main/examples/
1902+
See [examples](examples/).

0 commit comments

Comments
 (0)