mpbn 3.8__tar.gz → 4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mpbn might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: mpbn
3
- Version: 3.8
3
+ Version: 4.1
4
4
  Summary: Simple implementation of Most Permissive Boolean networks
5
5
  Home-page: https://github.com/bnediction/mpbn
6
6
  Author: Loïc Paulevé
@@ -15,9 +15,19 @@ Requires-Dist: boolean.py
15
15
  Requires-Dist: clingo
16
16
  Requires-Dist: colomoto_jupyter>=0.8.0
17
17
  Requires-Dist: numpy
18
- Requires-Dist: pyeda
18
+ Requires-Dist: biodivine_aeon>=1.0.1
19
19
  Requires-Dist: scipy
20
20
  Requires-Dist: tqdm
21
+ Dynamic: author
22
+ Dynamic: author-email
23
+ Dynamic: classifier
24
+ Dynamic: description
25
+ Dynamic: description-content-type
26
+ Dynamic: home-page
27
+ Dynamic: keywords
28
+ Dynamic: license
29
+ Dynamic: requires-dist
30
+ Dynamic: summary
21
31
 
22
32
 
23
33
  The `mpbn` Python module offers a simple implementation of reachability and attractor analysis (minimal trap spaces) in *Most Permissive Boolean Networks* ([doi:10.1038/s41467-020-18112-5](https://doi.org/10.1038/s41467-020-18112-5)). The `mpbn` Python module also offers a *Most Permissive* simulator, which provides trajectory sampling and computes attractor propensities (see paper [Variable-Depth Simulation of Most Permissive Boolean Networks](https://link.springer.com/chapter/10.1007/978-3-031-15034-0_7) for more details).
@@ -38,7 +48,7 @@ pip install mpbn
38
48
 
39
49
  ### Using conda
40
50
  ```
41
- conda install -c colomoto -c potassco mpbn
51
+ conda install -c colomoto -c potassco -c daemontus mpbn
42
52
  ```
43
53
 
44
54
  ## Usage
@@ -17,7 +17,7 @@ pip install mpbn
17
17
 
18
18
  ### Using conda
19
19
  ```
20
- conda install -c colomoto -c potassco mpbn
20
+ conda install -c colomoto -c potassco -c daemontus mpbn
21
21
  ```
22
22
 
23
23
  ## Usage
@@ -31,10 +31,6 @@ from colomoto import minibn
31
31
 
32
32
  from boolean import boolean
33
33
  import clingo
34
-
35
- from pyeda.boolalg import bdd
36
- import pyeda.boolalg.expr
37
- from pyeda.boolalg.expr import expr
38
34
  sys.setrecursionlimit(max(100000, sys.getrecursionlimit()))
39
35
 
40
36
  __asplibdir__ = os.path.realpath(os.path.join(os.path.dirname(__file__), "asplib"))
@@ -78,7 +74,7 @@ def s2v(s):
78
74
  def v2s(v):
79
75
  return 1 if v > 0 else 0
80
76
 
81
- def is_unate(ba, f):
77
+ def is_dnf_unate(ba, f):
82
78
  pos_lits = set()
83
79
  neg_lits = set()
84
80
  def is_lit(f):
@@ -116,34 +112,6 @@ def is_unate(ba, f):
116
112
  return test_monotonicity()
117
113
  return False
118
114
 
119
- def asp_of_bdd(bid, b):
120
- _rules = dict()
121
- def register(node, nid=None):
122
- if node is bdd.BDDNODEONE:
123
- if nid is not None:
124
- _rules[bid] = f"bdd({clingo.String(nid)},1)"
125
- return 1
126
- elif node is bdd.BDDNODEZERO:
127
- if nid is not None:
128
- _rules[bid] = f"bdd({clingo.String(nid)},-1)"
129
- return -1
130
- nid = clingo.String(f"{bid}_n{id(node)}" if nid is None else nid)
131
- if nid not in _rules:
132
- var = clingo.String(bdd._VARS[node.root].qualname)
133
- lo = register(node.lo)
134
- hi = register(node.hi)
135
- a = f"bdd({nid},{var},{lo},{hi})"
136
- _rules[nid] = a
137
- return nid
138
- register(b.node, bid)
139
- return _rules.values()
140
-
141
- def bddasp_of_boolfunc(f, i):
142
- e = expr(str(f).replace("!","~"))
143
- b = bdd.expr2bdd(e)
144
- atoms = asp_of_bdd(i, b)
145
- return "\n".join((f"{a}." for a in atoms))
146
-
147
115
  def circuitasp_of_boolfunc(f, i, ba):
148
116
  atoms = []
149
117
  fid = clingo.String(i)
@@ -176,28 +144,9 @@ def circuitasp_of_boolfunc(f, i, ba):
176
144
  atoms.append(f"circuit({fid},root,{root}).\n")
177
145
  return "\n".join(atoms)
178
146
 
179
-
180
- def expr2bpy(ex, ba):
181
- """
182
- converts a pyeda Boolean expression into a boolean.py one
183
- """
184
- if isinstance(ex, pyeda.boolalg.expr.Variable):
185
- return ba.Symbol(str(ex))
186
- elif isinstance(ex, pyeda.boolalg.expr._One):
187
- return ba.TRUE
188
- elif isinstance(ex, pyeda.boolalg.expr._Zero):
189
- return ba.FALSE
190
- elif isinstance(ex, pyeda.boolalg.expr.Complement):
191
- return ba.NOT(ba.Symbol(str(ex.__invert__())))
192
- elif isinstance(ex, pyeda.boolalg.expr.NotOp):
193
- return ba.NOT(expr2bpy(ex.x, ba))
194
- elif isinstance(ex, pyeda.boolalg.expr.OrOp):
195
- return ba.OR(*(expr2bpy(x, ba) for x in ex.xs))
196
- elif isinstance(ex, pyeda.boolalg.expr.AndOp):
197
- return ba.AND(*(expr2bpy(x, ba) for x in ex.xs))
198
- raise NotImplementedError(str(ex), type(ex))
199
-
200
147
  DEFAULT_ENCODING = "mixed-dnf-bdd"
148
+ DEFAULT_BOOLFUNCLIB = os.environ.get("MPBN_BOOLFUNCLIB", "aeon")
149
+ SUPPORTED_BOOLFUNCLIBS = ["aeon", "pyeda"]
201
150
 
202
151
  class MPBooleanNetwork(minibn.BooleanNetwork):
203
152
  """
@@ -218,7 +167,8 @@ class MPBooleanNetwork(minibn.BooleanNetwork):
218
167
  def __init__(self, bn=minibn.BooleanNetwork(), auto_dnf=True,
219
168
  simplify=False,
220
169
  try_unate_hard=False,
221
- encoding=DEFAULT_ENCODING):
170
+ encoding=DEFAULT_ENCODING,
171
+ boolfunclib=DEFAULT_BOOLFUNCLIB):
222
172
  """
223
173
  Constructor for :py:class:`.MPBoooleanNetwork`.
224
174
 
@@ -227,6 +177,9 @@ class MPBooleanNetwork(minibn.BooleanNetwork):
227
177
  :py:class:`colomoto.minibn.BooleanNetwork` constructor
228
178
  :param bool auto_dnf: if ``False``, turns off automatic DNF
229
179
  transformation of local functions
180
+ :param str boolfunlib: library to use for Boolean function manipulation
181
+ among ``"aeon"`` (default) or ``"pyeda"``. Default can be overriden with
182
+ ``MPBN_BOOLFUNCLIB`` environment variable.
230
183
 
231
184
  Examples:
232
185
 
@@ -236,11 +189,21 @@ class MPBooleanNetwork(minibn.BooleanNetwork):
236
189
  >>> mbn = MPBooleanNetwork(bn)
237
190
  """
238
191
  assert encoding in self.supported_encodings
192
+ assert boolfunclib in SUPPORTED_BOOLFUNCLIBS
239
193
  self.auto_dnf = auto_dnf and encoding in self.dnf_encodings
240
194
  self.encoding = encoding
241
195
  self.try_unate_hard = try_unate_hard
242
196
  self._simplify = simplify
243
197
  self._is_unate = dict()
198
+
199
+ self._boolfunclib = boolfunclib
200
+ __boolfunclib_symbols = (
201
+ "make_dnf_boolfunc",
202
+ "bddasp_of_boolfunc",
203
+ )
204
+ self._bf_impl = __import__(f"mpbn.boolfunclib.{boolfunclib}_impl",
205
+ fromlist=__boolfunclib_symbols)
206
+
244
207
  super(MPBooleanNetwork, self).__init__(bn)
245
208
 
246
209
  def __setitem__(self, a, f):
@@ -253,18 +216,12 @@ class MPBooleanNetwork(minibn.BooleanNetwork):
253
216
  f = self.ba.parse(f)
254
217
  f = self._autobool(f)
255
218
  if self.auto_dnf:
256
- e = expr(str(f).replace("!","~"))
257
- e = e.to_dnf()
258
- if self._simplify is not None:
259
- e = e.simplify()
260
- f = expr2bpy(e, self.ba)
261
- if self.try_unate_hard:
262
- f = minibn.simplify_dnf(self.ba, f)
263
- elif self._simplify:
264
- f = f.simplify()
219
+ f = self._bf_impl.make_dnf_boolfunc(self.ba, f,
220
+ simplify=self._simplify,
221
+ try_unate_hard=self.try_unate_hard)
265
222
  a = self._autokey(a)
266
223
  if self.encoding in self.dnf_encodings:
267
- self._is_unate[a] = is_unate(self.ba, f)
224
+ self._is_unate[a] = is_dnf_unate(self.ba, f)
268
225
  if self.encoding == "unate-dnf":
269
226
  assert self._is_unate[a], f"'{f}' seems not unate. Try simplify()?"
270
227
  return super().__setitem__(a, f)
@@ -311,13 +268,13 @@ class MPBooleanNetwork(minibn.BooleanNetwork):
311
268
  elif f_encoding == "dnf":
312
269
  facts.extend(encode_dnf(f))
313
270
  elif f_encoding == "bdd":
314
- facts.append(bddasp_of_boolfunc(f, n))
271
+ facts.append(self._bf_impl.bddasp_of_boolfunc(self.ba, f, n))
315
272
  elif f_encoding == "mixed-dnf-bdd":
316
273
  facts.extend(encode_dnf(f))
317
274
  if self._is_unate[n]:
318
275
  facts.append(f"unate(\"{n}\").")
319
276
  else:
320
- facts.append(bddasp_of_boolfunc(f, n))
277
+ facts.append(self._bf_impl.bddasp_of_boolfunc(self.ba, f, n))
321
278
  elif f_encoding == "circuit":
322
279
  facts.append(circuitasp_of_boolfunc(f, n, self.ba))
323
280
  return "".join(facts)
File without changes
@@ -0,0 +1,202 @@
1
+ import networkx as nx
2
+ import clingo
3
+
4
+ from boolean import boolean
5
+ from colomoto import minibn
6
+
7
+ from typing import Optional
8
+
9
+ from biodivine_aeon import Bdd, BddPointer
10
+ from biodivine_aeon import BddVariableSet, BddValuation
11
+
12
+ def is_unate_symbolic(f: Bdd) -> boolean:
13
+ """
14
+ Returns `True` if the given `biodivine_aeon.Bdd` represents a unate function
15
+ (i.e. all arguments are locally monotonic).
16
+
17
+ The way this is handled is that we test for positive/negative monotonicity by
18
+ symbolically expressing the inputs where decreasing the input increases the
19
+ output (i.e. a counterexample to positive monotonicity), or vice versa.
20
+ """
21
+ variables = f.__ctx__().variable_ids()
22
+ f_false = f.l_not()
23
+ f_true = f
24
+ for var in f.support_set():
25
+ var_is_true = f.__ctx__().mk_literal(var, True)
26
+ var_is_false = f.__ctx__().mk_literal(var, False)
27
+
28
+ f_1_to_0 = f_false.l_and(var_is_true).r_exists(var)
29
+ f_0_to_1 = f_true.l_and(var_is_false).r_exists(var)
30
+ is_positive = f_0_to_1.l_and(f_1_to_0).r_exists(variables).l_not().is_true()
31
+
32
+ f_0_to_0 = f_false.l_and(var_is_false).r_exists(var)
33
+ f_1_to_1 = f_true.l_and(var_is_true).r_exists(var)
34
+ is_negative = f_0_to_0.l_and(f_1_to_1).r_exists(variables).l_not().is_true()
35
+
36
+ # An input cannot be both positive and negative at the same time.
37
+ assert not (is_positive and is_negative)
38
+
39
+ if (not is_positive) and (not is_negative):
40
+ return False
41
+ return True
42
+
43
+
44
+ def ba_to_bdd(ba: boolean.BooleanAlgebra, f: boolean.Expression, ctx: BddVariableSet | None = None) -> Bdd:
45
+ """
46
+ Takes a `boolean.Expression` (with the associated `boolean.BooleanAlgebra`) and
47
+ converts it to a `biodivine_aeon.Bdd`.
48
+
49
+ Note that the `Bdd` has an associated `biodivine_aeon.BddVariableSet` context, which maps the
50
+ variable IDs to names. You can provide your own context, or one will be created for you
51
+ (to access the underlying context object, use `bdd.__ctx__()`).
52
+ """
53
+ ba_vars = f.symbols
54
+ variables = sorted([ str(var) for var in ba_vars ])
55
+ if ctx is None:
56
+ ctx = BddVariableSet(variables)
57
+ else:
58
+ # Check that all variables that exist in `f` also exist in `ctx`.
59
+ assert all((ctx.find_variable(var) is not None) for var in variables)
60
+ def ba_to_bdd_rec(f: boolean.Expression) -> Bdd:
61
+ if type(f) is ba.TRUE or isinstance(f, boolean._TRUE):
62
+ return ctx.mk_const(True)
63
+ if type(f) is ba.FALSE or isinstance(f, boolean._FALSE):
64
+ return ctx.mk_const(False)
65
+ if type(f) is ba.Symbol:
66
+ return ctx.mk_literal(str(f.obj), True)
67
+ if type(f) is ba.NOT:
68
+ assert len(f.args) == 1, "Cannot transform NOT with more than one argument."
69
+ return ba_to_bdd_rec(f.args[0]).l_not()
70
+ if type(f) is ba.AND:
71
+ result = ctx.mk_const(True)
72
+ for arg in f.args:
73
+ result = result.l_and(ba_to_bdd_rec(arg))
74
+ return result
75
+ if type(f) is ba.OR:
76
+ result = ctx.mk_const(False)
77
+ for arg in f.args:
78
+ result = result.l_or(ba_to_bdd_rec(arg))
79
+ return result
80
+ raise NotImplementedError(str(f), type(f))
81
+
82
+ return ba_to_bdd_rec(f)
83
+
84
+ def bdd_to_dnf(ba: boolean.BooleanAlgebra, f: Bdd) -> boolean.Expression:
85
+ """
86
+ Convert a `biodivine_aeon.Bdd` to a `boolean.Expression` in disjunctive normal form.
87
+ """
88
+ if f.is_true():
89
+ return ba.TRUE
90
+ if f.is_false():
91
+ return ba.FALSE
92
+ ctx = f.__ctx__()
93
+ # Technically, `optimize=True` should be set by default, but just in case.
94
+ dnf = f.to_dnf(optimize=True)
95
+ # Maps BDD variables to BooleanAlgebra Symbols.
96
+ var_to_symbol = { var: ba.Symbol(ctx.get_variable_name(var)) for var in ctx.variable_ids() }
97
+ ba_clauses = []
98
+ for clause in dnf:
99
+ literals = []
100
+ for (var, value) in clause.items():
101
+ if value:
102
+ literals.append(var_to_symbol[var])
103
+ else:
104
+ literals.append(ba.NOT(var_to_symbol[var]))
105
+ assert len(literals) > 0
106
+ if len(literals) == 1:
107
+ ba_clauses.append(literals[0])
108
+ else:
109
+ ba_clauses.append(ba.AND(*literals))
110
+ assert len(ba_clauses) > 0
111
+ if len(ba_clauses) == 1:
112
+ return ba_clauses[0]
113
+ else:
114
+ return ba.OR(*ba_clauses)
115
+
116
+ def make_dnf_boolfunc(ba, f, **unused_opts):
117
+ bdd = ba_to_bdd(ba, f)
118
+ return bdd_to_dnf(ba, bdd)
119
+
120
+ def asp_of_bdd(var_name, bdd: Bdd) -> list[str]:
121
+ """
122
+ Convert a `biodivine_aeon.Bdd` into a list of `clingo` atoms
123
+ representing the individual BDD nodes.
124
+ """
125
+ if bdd.is_false():
126
+ return [f"bdd({clingo.String(var_name)},-1)"]
127
+ if bdd.is_true():
128
+ return [f"bdd({clingo.String(var_name)},1)"]
129
+
130
+ _rules = {}
131
+ def _rec(node: BddPointer, node_name: Optional[str] = None) -> str:
132
+ if node.is_zero():
133
+ return "-1"
134
+ if node.is_one():
135
+ return "1"
136
+ if node_name is None:
137
+ node_name = f"{var_name}_n{int(node)}"
138
+ node_name_clingo = clingo.String(node_name)
139
+ if node_name_clingo in _rules:
140
+ # The node was already declared.
141
+ return node_name_clingo
142
+ node_var = bdd.node_variable(node)
143
+ assert node_var is not None # Only `None` if node is terminal.
144
+ (lo, hi) = bdd.node_links(node)
145
+ var = clingo.String(bdd.__ctx__().get_variable_name(node_var))
146
+ lo = _rec(lo)
147
+ hi = _rec(hi)
148
+ atom = f"bdd({node_name_clingo},{var},{lo},{hi})"
149
+ _rules[node_name_clingo] = atom
150
+ return node_name_clingo
151
+ _rec(bdd.root(), var_name)
152
+
153
+ return list(_rules.values())
154
+
155
+ def bddasp_of_boolfunc(ba, f, var_name):
156
+ f_bdd = ba_to_bdd(ba, f)
157
+ atoms = asp_of_bdd(var_name, f_bdd)
158
+ return "\n".join((f"{a}." for a in atoms))
159
+
160
+ def bn_of_asynchronous_transition_graph(adyn, names,
161
+ parse_node=(lambda n: tuple(map(int, n))),
162
+ bn_class=minibn.BooleanNetwork,
163
+ simplify=True):
164
+ """
165
+ Convert the transition graph of a (fully) asynchronous Boolean network to
166
+ a propositional logic representation.
167
+
168
+ The object `adyn` must be an instance of `networkx.DiGraph`.
169
+ The `parse_node` function must return a tuple of 0 and 1 from an `adyn`
170
+ node. By default, it is assumed that nodes are strings of binary values.
171
+ Returned object will be of `bn_class`, instantiated with a dictionnary
172
+ mapping component names to a string representation of their Boolean expression.
173
+ """
174
+ relabel = {label: parse_node(label) for label in adyn.nodes()}
175
+ adyn = nx.relabel_nodes(adyn, relabel)
176
+ n = len(next(iter(adyn.nodes)))
177
+ assert n == len(names), "list of component names and dimension of configuraitons seem different"
178
+ assert adyn.number_of_nodes() == 2**n, "unexpected number of nodes in the transition graph"
179
+
180
+ bdd_ctx = BddVariableSet(names)
181
+
182
+ f = []
183
+ for i in range(n):
184
+ pos = []
185
+ for x in adyn.nodes():
186
+ dx = list(x)
187
+ dx[i] = 1-x[i]
188
+ y = dx if tuple(dx) in adyn[x] else x
189
+ target = y[i]
190
+ if target:
191
+ pos.append(BddValuation(bdd_ctx, list(x)))
192
+ if len(pos) == 0:
193
+ f.append(bdd_ctx.mk_false())
194
+ else:
195
+ f.append(bdd_ctx.mk_dnf(pos))
196
+
197
+ bn = bn_class()
198
+ for (i, name) in enumerate(names):
199
+ bn[name] = bdd_to_dnf(bn.ba, f[i])
200
+ if simplify:
201
+ bn = bn.simplify()
202
+ return bn
@@ -1,7 +1,10 @@
1
+ import clingo
1
2
  import networkx as nx
3
+
2
4
  from pyeda.boolalg.minimization import *
3
5
  import pyeda.boolalg.expr
4
6
  from pyeda.inter import expr
7
+ from pyeda.boolalg import bdd
5
8
 
6
9
  from colomoto import minibn
7
10
 
@@ -29,6 +32,68 @@ def expr2str(ex):
29
32
  return " & ".join(map(_protect, ex.xs))
30
33
  raise NotImplementedError(str(ex), type(ex))
31
34
 
35
+ def expr2bpy(ex, ba):
36
+ """
37
+ converts a pyeda Boolean expression into a boolean.py one
38
+ """
39
+ if isinstance(ex, pyeda.boolalg.expr.Variable):
40
+ return ba.Symbol(str(ex))
41
+ elif isinstance(ex, pyeda.boolalg.expr._One):
42
+ return ba.TRUE
43
+ elif isinstance(ex, pyeda.boolalg.expr._Zero):
44
+ return ba.FALSE
45
+ elif isinstance(ex, pyeda.boolalg.expr.Complement):
46
+ return ba.NOT(ba.Symbol(str(ex.__invert__())))
47
+ elif isinstance(ex, pyeda.boolalg.expr.NotOp):
48
+ return ba.NOT(expr2bpy(ex.x, ba))
49
+ elif isinstance(ex, pyeda.boolalg.expr.OrOp):
50
+ return ba.OR(*(expr2bpy(x, ba) for x in ex.xs))
51
+ elif isinstance(ex, pyeda.boolalg.expr.AndOp):
52
+ return ba.AND(*(expr2bpy(x, ba) for x in ex.xs))
53
+ raise NotImplementedError(str(ex), type(ex))
54
+
55
+ def asp_of_bdd(bid, b):
56
+ _rules = dict()
57
+ def register(node, nid=None):
58
+ if node is bdd.BDDNODEONE:
59
+ if nid is not None:
60
+ _rules[bid] = f"bdd({clingo.String(nid)},1)"
61
+ return 1
62
+ elif node is bdd.BDDNODEZERO:
63
+ if nid is not None:
64
+ _rules[bid] = f"bdd({clingo.String(nid)},-1)"
65
+ return -1
66
+ nid = clingo.String(f"{bid}_n{id(node)}" if nid is None else nid)
67
+ if nid not in _rules:
68
+ var = clingo.String(bdd._VARS[node.root].qualname)
69
+ lo = register(node.lo)
70
+ hi = register(node.hi)
71
+ a = f"bdd({nid},{var},{lo},{hi})"
72
+ _rules[nid] = a
73
+ return nid
74
+ register(b.node, bid)
75
+ return _rules.values()
76
+
77
+ def bddasp_of_boolfunc(ba, f, i):
78
+ e = expr(str(f).replace("!","~"))
79
+ b = bdd.expr2bdd(e)
80
+ atoms = asp_of_bdd(i, b)
81
+ return "\n".join((f"{a}." for a in atoms))
82
+
83
+ def make_dnf_boolfunc(ba, f, try_unate_hard=False, simplify=True):
84
+ """
85
+ try_unate_hard: use costly CNF/DNF transformations
86
+ simplify: use boolean.py simplification method
87
+ """
88
+ e = expr(str(f).replace("!","~"))
89
+ e = e.to_dnf()
90
+ e = e.simplify()
91
+ e = expr2bpy(e, ba)
92
+ if try_unate_hard:
93
+ e = minibn.simplify_dnf(self.ba, e)
94
+ elif simplify:
95
+ e = e.simplify()
96
+ return e
32
97
 
33
98
  def bn_of_asynchronous_transition_graph(adyn, names,
34
99
  parse_node=(lambda n: tuple(map(int, n))),
@@ -76,12 +141,3 @@ def bn_of_asynchronous_transition_graph(adyn, names,
76
141
  f = f.simplify()
77
142
  return f
78
143
 
79
- if __name__ == "__main__":
80
- import mpbn
81
-
82
- f = mpbn.MPBooleanNetwork({
83
- "x1": "x2",
84
- "x2": "x3",
85
- "x3": "x1"})
86
- g = f.dynamics("asynchronous")
87
- print(bn_of_asynchronous_transition_graph(g, list(f)))
@@ -17,6 +17,9 @@ def main():
17
17
  ap.add_argument("--encoding", default=mpbn.DEFAULT_ENCODING,
18
18
  choices=mpbn.MPBooleanNetwork.supported_encodings,
19
19
  help=f"Encoding method (default: {mpbn.DEFAULT_ENCODING})")
20
+ ap.add_argument("--boolfunclib", default="aeon",
21
+ choices=mpbn.SUPPORTED_BOOLFUNCLIBS,
22
+ help=f"Backend lib for Boolean functions (default: {mpbn.DEFAULT_BOOLFUNCLIB})")
20
23
  ap.add_argument("--input-is-dnf", action="store_true", default=False,
21
24
  help="Functions are already in DNF form")
22
25
  ap.add_argument("--simplify", action="store_true", default=False,
@@ -27,6 +30,7 @@ def main():
27
30
  help="Returns only the number of solutions")
28
31
  args = ap.parse_args()
29
32
  mbn = mpbn.MPBooleanNetwork(args.bnet_file, encoding=args.encoding,
33
+ boolfunclib=args.boolfunclib,
30
34
  auto_dnf=not args.input_is_dnf,
31
35
  simplify=args.simplify,
32
36
  try_unate_hard=args.try_unate_hard)
@@ -0,0 +1,14 @@
1
+ try:
2
+ from mpbn.boolfunclib.aeon_impl import bn_of_asynchronous_transition_graph
3
+ except ImportError:
4
+ from mpbn.boolfunclib.pyeda_impl import bn_of_asynchronous_transition_graph
5
+
6
+ if __name__ == "__main__":
7
+ import mpbn
8
+
9
+ f = mpbn.MPBooleanNetwork({
10
+ "x1": "x2",
11
+ "x2": "x3",
12
+ "x3": "x1"})
13
+ g = f.dynamics("asynchronous")
14
+ print(bn_of_asynchronous_transition_graph(g, list(f)))
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: mpbn
3
- Version: 3.8
3
+ Version: 4.1
4
4
  Summary: Simple implementation of Most Permissive Boolean networks
5
5
  Home-page: https://github.com/bnediction/mpbn
6
6
  Author: Loïc Paulevé
@@ -15,9 +15,19 @@ Requires-Dist: boolean.py
15
15
  Requires-Dist: clingo
16
16
  Requires-Dist: colomoto_jupyter>=0.8.0
17
17
  Requires-Dist: numpy
18
- Requires-Dist: pyeda
18
+ Requires-Dist: biodivine_aeon>=1.0.1
19
19
  Requires-Dist: scipy
20
20
  Requires-Dist: tqdm
21
+ Dynamic: author
22
+ Dynamic: author-email
23
+ Dynamic: classifier
24
+ Dynamic: description
25
+ Dynamic: description-content-type
26
+ Dynamic: home-page
27
+ Dynamic: keywords
28
+ Dynamic: license
29
+ Dynamic: requires-dist
30
+ Dynamic: summary
21
31
 
22
32
 
23
33
  The `mpbn` Python module offers a simple implementation of reachability and attractor analysis (minimal trap spaces) in *Most Permissive Boolean Networks* ([doi:10.1038/s41467-020-18112-5](https://doi.org/10.1038/s41467-020-18112-5)). The `mpbn` Python module also offers a *Most Permissive* simulator, which provides trajectory sampling and computes attractor propensities (see paper [Variable-Depth Simulation of Most Permissive Boolean Networks](https://link.springer.com/chapter/10.1007/978-3-031-15034-0_7) for more details).
@@ -38,7 +48,7 @@ pip install mpbn
38
48
 
39
49
  ### Using conda
40
50
  ```
41
- conda install -c colomoto -c potassco mpbn
51
+ conda install -c colomoto -c potassco -c daemontus mpbn
42
52
  ```
43
53
 
44
54
  ## Usage
@@ -15,5 +15,8 @@ mpbn/asplib/eval_mixed.asp
15
15
  mpbn/asplib/mp_attractor.asp
16
16
  mpbn/asplib/mp_eval.asp
17
17
  mpbn/asplib/mp_positivereach-np.asp
18
+ mpbn/boolfunclib/__init__.py
19
+ mpbn/boolfunclib/aeon_impl.py
20
+ mpbn/boolfunclib/pyeda_impl.py
18
21
  mpbn/cli/__init__.py
19
22
  mpbn/cli/sim.py
@@ -2,6 +2,6 @@ boolean.py
2
2
  clingo
3
3
  colomoto_jupyter>=0.8.0
4
4
  numpy
5
- pyeda
5
+ biodivine_aeon>=1.0.1
6
6
  scipy
7
7
  tqdm
@@ -4,7 +4,7 @@
4
4
  from setuptools import setup, find_packages
5
5
 
6
6
  NAME = "mpbn"
7
- VERSION = "3.8"
7
+ VERSION = "4.1"
8
8
 
9
9
  setup(name=NAME,
10
10
  version=VERSION,
@@ -27,7 +27,7 @@ setup(name=NAME,
27
27
  "clingo",
28
28
  "colomoto_jupyter>=0.8.0",
29
29
  "numpy",
30
- "pyeda",
30
+ "biodivine_aeon>=1.0.1",
31
31
  "scipy",
32
32
  "tqdm"
33
33
  ],
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes