This is SNAKES' main module, it holds the various Petri net elements: arcs, places, transitions, markings, nets themselves and marking graphs.
Auxiliary definitions
Class Evaluator
class Evaluator (object) :
Evaluate expression or execute statements in shareable
namespace. Instances of this class can be found in particular as
attribute globals
of many objects among which PetriNet
.
Method Evaluator.__init__
def __init__ (self, *larg, **karg) :
Initialize like a dict
Method Evaluator.__call__
def __call__ (self, expr, locals={}) :
Evaluate an expression, this is somehow equivalent to
eval(expr, self, locals)
Call API
str expr
: an expression suitable toeval
dict locals
: additional environment for local namesreturn object
: the result of the evaluation
Method Evaluator.declare
def declare (self, stmt, locals=None) :
Execute a statement, this is somehow equivalent to
exec(stmt, self, locals)
Call API
str stmt
: a statement suitable toexec
dict locals
: additional environment for local names
Method Evaluator.update
def update (self, other) :
Update namespace from another evaluator or a dict
Call API
Evaluator other
: another evaluator or a dict
Method Evaluator.copy
def copy (self) :
Copy the evaluator and its namespace
Call API
return Evaluator
: a copy of the evaluator
Method Evaluator.__contains__
def __contains__ (self, name) :
Test if a name is declared, like dict.__contains__
Method Evaluator.__getitem__
def __getitem__ (self, key) :
Return an item from namespace, like dict.__getitem__
but
keys must be strings (because they are names)
Method Evaluator.__setitem__
def __setitem__ (self, key, val) :
Set an item in namespace, like dict.__setitem__
but keys
must be strings (because they are names)
Method Evaluator.__iter__
def __iter__ (self) :
Iterate over namespace items (key/value pairs), like
dict.items
Method Evaluator.__eq__
def __eq__ (self, other) :
Test for equality of namespaces
Method Evaluator.__ne__
def __ne__ (self, other) :
Test for inequality of namespaces
Class NetElement
class NetElement (object) :
The base class for Petri net elements. This class is abstract and should not be instanciated.
Class Token
class Token (NetElement) :
A container for one token value. This class is intended for internal use only in order to avoid some confusion when inserting arbitrary values in a place. End users will probably never need to use it directly.
Method Token.__init__
def __init__ (self, value) :
Initialise the token
>>> Token(42)
Token(42)
Call API
object value
: value of the token
Class BlackToken
class BlackToken (Token) :
The usual black token, an instance is available as member dot
of module snakes.nets
, which is also its string representation.
Another object tBlackToken
is available to be used as a place
type.
>>> BlackToken()
dot
>>> tBlackToken
Instance(BlackToken)
Arcs annotations
Class ArcAnnotation
class ArcAnnotation (NetElement) :
An annotation on an arc.
On input arcs (from a place to a transition), annotations may be values or variables, potentially nested in tuples. On output arcs (from a transition to a place), expressions are allowed, which corresponds to a computation.
A class attribute input_allowed
is set to True
or False
according to whether an annotation is allowed on input arcs.
This class is abstract and should not be instantiated.
Method ArcAnnotation.copy
def copy (self) :
Return a copy of the annotation.
Method ArcAnnotation.substitute
def substitute (self, binding) :
Substitutes the variables in the annotation.
Call API
Substitution binding
: the substitution to apply
Method ArcAnnotation.replace
def replace (self, old, new) :
Returns a copy of the annotation in which the old
annotation
has been replaced by the new
one.
With non composite annotation, this will return a copy of the
annotation if it is not the same as old
otherwise it returns
new
. Composite annotations will replace there components.
>>> Value(2).replace(Variable('x'), Value(5))
Value(2)
>>> Variable('x').replace(Variable('x'), Value(5))
Value(5)
>>> MultiArc([Value(2), Variable('x')]).replace(Variable('x'), Value(5))
MultiArc((Value(2), Value(5)))
>>> Test(Value(2)).replace(Variable('x'), Value(5))
Test(Value(2))
>>> Test(Variable('x')).replace(Variable('x'), Value(5))
Test(Value(5))
Call API
ArcAnnotation old
: the annotation to be replacedArcAnnotation new
: the annotation to use instead ofold
Method ArcAnnotation.vars
def vars (self) :
Return the list of variables involved in the annotation.
Method ArcAnnotation.bind
def bind (self, binding) :
Return the value of the annotation evaluated through
binding
.
>>> Expression('x+1').bind(Substitution(x=2))
Token(3)
Call API
Substitution binding
: a substitutionreturn object
: a value
Method ArcAnnotation.check
def check (self, binding, tokens) :
Check whether the label allows to fire with tokens
>>> Value(1).check(Substitution(), MultiSet([1, 2, 3]))
True
>>> Value(4).check(Substitution(), MultiSet([1, 2, 3]))
False
>>> Variable("x").check(Substitution(x=1), MultiSet([1, 2, 3]))
True
>>> Variable("x").check(Substitution(x=4), MultiSet([1, 2, 3]))
False
Method ArcAnnotation.flow
def flow (self, binding) :
Return the flow of tokens implied by the annotation evaluated
through binding
.
>>> Value(1).flow(Substitution(x=2))
MultiSet([1])
Call API
Substitution binding
: a substitutionreturn object
: a multiset of values
Method ArcAnnotation.modes
def modes (self, values) :
Return the list of modes under which an arc with the
annotation may flow the tokens in values
. Each mode is a
substitution indicating how to bind the annotation.
>>> Variable('x').modes([1, 2, 3])
[Substitution(x=1), Substitution(x=2), Substitution(x=3)]
Call API
collection values
: a collection of valuesreturn object
: a list of substitutions
Class Value
class Value (ArcAnnotation) :
A single token value.
Method Value.__init__
def __init__ (self, value) :
Initialise with the encapsulated value.
Call API
object value
: the value of the token.
Method Value.copy
def copy (self) :
Return a copy of the value.
>>> Value(5).copy()
Value(5)
Call API
return Value
: a copy of the value
Method Value.vars
def vars (self) :
Return the list of variables involved in the value (empty).
>>> Value(42).vars()
[]
Call API
return list
: empty list
Method Value.bind
def bind (self, binding) :
Return the value evaluated through binding
(which is the
value itself).
>>> Value(1).bind(Substitution(x=2))
Token(1)
Call API
Substitution binding
: a substitutionreturn object
: a value
Method Value.modes
def modes (self, values) :
Return an empty binding (no binding needed for values) iff the
value is in values
, raise ModeError otherwise.
>>> Value(1).modes([1, 2, 3])
[Substitution()]
>>> try : Value(1).modes([2, 3, 4])
... except ModeError : print(sys.exc_info()[1])
no match for value
Call API
collection values
: a collection of valuesreturn list
: a list of substitutions
Exceptions
ModeError
: when no suitable mode can be found
Method Value.substitute
def substitute (self, binding) :
Bind the value (nothing to do).
>>> v = Value(5)
>>> v.substitute(Substitution(x=5))
>>> v
Value(5)
Call API
Substitution binding
: a substitution
Class Variable
class Variable (ArcAnnotation) :
A variable which may be bound to a token.
Method Variable.__init__
def __init__ (self, name) :
Variable names must start with a letter and may continue with any alphanumeric character.
>>> Variable('x')
Variable('x')
>>> try : Variable('_test')
... except ValueError: print(sys.exc_info()[1])
not a variable name '_test'
Call API
str name
: the name of the variable
Method Variable.copy
def copy (self) :
Return a copy of the variable.
>>> Variable('x').copy()
Variable('x')
Call API
return Variable
: a copy of the variable
Method Variable.rename
def rename (self, name) :
Change the name of the variable.
>>> v = Variable('x')
>>> v.rename('y')
>>> v
Variable('y')
Call API
str name
: the new name of the variable
Method Variable.modes
def modes (self, values) :
Return the the list of substitutions mapping the name of the
variable to each value in values
.
>>> Variable('x').modes(range(3))
[Substitution(x=0), Substitution(x=1), Substitution(x=2)]
Call API
collection values
: a collection of valuesreturn list
: a list of substitutions
Exceptions
ModeError
: when no suitable mode can be found
Method Variable.bind
def bind (self, binding) :
Return the value of the variable evaluated through binding
.
>>> Variable('x').bind(Substitution(x=3))
Token(3)
>>> try : Variable('x').bind(Substitution(z=3))
... except DomainError : print(sys.exc_info()[1])
unbound variable 'x'
Call API
Substitution binding
: a substitutionreturn object
: a value
Method Variable.substitute
def substitute (self, binding) :
Change the name according to binding
.
>>> v = Variable('x')
>>> v.substitute(Substitution(x='y'))
>>> v
Variable('y')
>>> v.substitute(Substitution(x='z'))
>>> v
Variable('y')
>>> v.rename('z')
>>> v
Variable('z')
Call API
Substitution binding
: a substitution
Method Variable.vars
def vars (self) :
Return variable name in a list.
>>> Variable('x').vars()
['x']
Call API
return list
: a list holding the name of the variable
Class Expression
class Expression (ArcAnnotation) :
An arbitrary Python expression which may be evaluated.
Each expression has its private namespace which can be extended or changed.
Method Expression.__init__
def __init__ (self, expr) :
The expression is compiled so its syntax is checked.
Call API
str expr
: a Python expression suitable foreval
Method Expression.copy
def copy (self) :
Return a copy of the expression.
Method Expression.bind
def bind (self, binding) :
Evaluate the expression through binding
.
>>> e = Expression('x*(y-1)')
>>> e.bind(Substitution(x=2, y=3))
Token(4)
>>> e.bind(Substitution(x=2))
Traceback (most recent call last):
...
NameError: name 'y' is not defined
Call API
Substitution binding
: a substitutionreturn object
: a value
Exceptions
NameError
: when the domain of the substitution does not allow to bind all the names in the expression (so it cannot be evaluated)
Method Expression.__call__
def __call__ (self, binding) :
Returns the value from bind
(but not encapsulated in a
Token
).
Method Expression.substitute
def substitute (self, binding) :
Substitute the variables according to 'binding'.
>>> e = Expression('x+1*xy')
>>> e.substitute(Substitution(x='y'))
>>> e
Expression('(y + (1 * xy))')
As we can see, the substitution adds a lot of parentheses and
spaces to the expression. But is really parses the expression
and makes no confusion is a substritued name appears as a
substrung of another name (like y
that appears in xy
above).
Call API
Substitution binding
: a substitution
Method Expression.vars
def vars (self) :
Return the list of variable names involved in the expression.
>>> list(sorted(Expression('x+y').vars()))
['x', 'y']
Call API
return list
: a list holding the names of the variables
Method Expression.__and__
def __and__ (self, other) :
Implement &
to perform and
between two expressions.
>>> Expression('x==1') & Expression('y==2')
Expression('(x==1) and (y==2)')
Minor optimisation are implemented:
>>> Expression('True') & Expression('x==y')
Expression('x==y')
>>> Expression('x==y') & Expression('True')
Expression('x==y')
and
Call API
Expression other
: an expressionreturn Expression
: an expression
Method Expression.__or__
def __or__ (self, other) :
Implement |
to perform or
between two expressions.
>>> Expression('x==1') | Expression('y==2')
Expression('(x==1) or (y==2)')
Minor optimisation are implemented:
>>> Expression('True') | Expression('x==y')
Expression('True')
>>> Expression('x==y') | Expression('True')
Expression('True')
or
Call API
Expression other
: an expressionreturn Expression
: an expression
Method Expression.__invert__
def __invert__ (self) :
Implement ~
to perform not
.
>>> ~Expression('x==1')
Expression('not (x==1)')
Minor optimisation are implemented:
>>> ~Expression('True')
Expression('False')
>>> ~Expression('False')
Expression('True')
not
Call API
return Expression
: an expression
Method Expression.__eq__
def __eq__ (self, other) :
Method Expression.__hash__
def __hash__ (self) :
Class MultiArc
class MultiArc (ArcAnnotation) :
A collection of other annotations, allowing to consume or produce several tokens at once.
Such a collection is allowed on input arcs only if so are all its components.
Method MultiArc.__init__
def __init__ (self, components) :
Initialise with the components of the tuple.
>>> MultiArc((Value(1), Expression('x+1')))
MultiArc((Value(1), Expression('x+1')))
>>> MultiArc((Value(1), Expression('x+1'))).input_allowed
False
>>> MultiArc((Value(1), Variable('x'))).input_allowed
True
Call API
collection components
: a list of components
Method MultiArc.copy
def copy (self) :
Return a copy of the multi-arc.
Method MultiArc.__iter__
def __iter__ (self) :
Iterate over the components.
Method MultiArc.__len__
def __len__ (self) :
Return the number of components
Method MultiArc.__contains__
def __contains__ (self, item) :
Test if an item is part of the multi-arc
Method MultiArc.flow
def flow (self, binding) :
Method MultiArc.bind
def bind (self, binding) :
Return the value of the annotation evaluated through
binding
.
>>> t = MultiArc((Value(1), Variable('x'), Variable('y')))
>>> t.bind(Substitution(x=2, y=3))
(Token(1), Token(2), Token(3))
Call API
Substitution binding
: a substitutionreturn object
: a tuple of value
Method MultiArc.modes
def modes (self, values) :
Return the list of modes under which an arc with the
annotation may flow the tokens in values
. Each mode is a
substitution indicating how to bind the annotation.
>>> t = MultiArc((Value(1), Variable('x'), Variable('y')))
>>> m = t.modes([1, 2, 3])
>>> len(m)
2
>>> Substitution(y=3, x=2) in m
True
>>> Substitution(y=2, x=3) in m
True
Call API
collection values
: a collection of valuesreturn object
: a list of substitutions
Method MultiArc.substitute
def substitute (self, binding) :
Substitute each component according to 'binding'.
>>> t = MultiArc((Value(1), Variable('x'), Variable('y')))
>>> t.substitute(Substitution(x='z'))
>>> t
MultiArc((Value(1), Variable('z'), Variable('y')))
Call API
Substitution binding
: a substitution
Method MultiArc.vars
def vars (self) :
Return the list of variables involved in the components.
>>> t = MultiArc((Value(1), Variable('x'), Variable('y')))
>>> list(sorted(t.vars()))
['x', 'y']
Call API
return set
: the set of variables
Method MultiArc.replace
def replace (self, old, new) :
Returns a copy of the annotation in which the old
annotation
has been replaced by the new
one.
With MultiArc
, replaces each occurrence of old
by new
Call API
ArcAnnotation old
: the annotation to be replacedArcAnnotation new
: the annotation to use instead ofold
Class Tuple
class Tuple (MultiArc) :
An annotation that that is a tuple of other annotations. This
is a subclass of MultiArc
only for programming convenience,
actually, a Tuple
instance carry a single token that is a
tuple
.
>>> t = Tuple((Value(1), Variable('x'), Variable('y')))
>>> m = t.modes([1, 2, (1, 2, 3), (3, 4, 5), (1, 2), (1, 2, 3, 4)])
>>> m == [Substitution(x=2, y=3)]
True
As we can see, this binds the variables inside the tuple, but only
token (1, 2, 3)
is correct to match the annotation. Note that
tuples may be nested arbitrarily.
Class Test
class Test (ArcAnnotation) :
This is a test arc, that behaves like another arc annotation
but never transport tokens. It is obtained by encapsulating the
other annotation and behaves exactly like the encapsulated
annotation except for method flow
that always returns an empty
multiset.
Method Test.__init__
def __init__ (self, annotation) :
Make a test arc from annotation
.
Method Test.copy
def copy (self) :
Return a copy of the test arc.
Method Test.flow
def flow (self, binding) :
Return the flow of tokens implied by the annotation evaluated
through binding
. For test arcs, this is alwas empty.
>>> Test(Value(1)).flow(Substitution())
MultiSet([])
Call API
Substitution binding
: a substitutionreturn object
: an empty multiset
Class Inhibitor
class Inhibitor (Test) :
This is an inhibitor arc that forbids the presence of some tokens in a place.
Method Inhibitor.__init__
def __init__ (self, annotation, condition=None) :
Like Test
, it works by encapsulating another annotation.
Additionally, a condition may be given which allows to select
the forbidden tokens more precisely. This is generally better
to make this selection at this level rather that at the level
of a transition guard: indeed, in the latter case, we may
build many modes and reject most of them because of the guard;
while in the former case, we will not build these modes at
all.
For instance:
Inhibitor(Value(3))
ensures that there is no token whose value is3
in the place when the transition is firedInhibitor(Variable('x'))
ensures that there is no token at all in the placeInhibitor(Variable('x'), Expression('x<3'))
ensures that there is no token whose value is less that3
Inhibitor(MultiArc([Variable('x'), Variable('y')]), Expression('x>y'))
ensures that there is no pair of tokens such that one is greater that the other
Method Inhibitor.bind
def bind (self, binding) :
Return no tokens since arc corresponds to an absence of tokens.
Raise ValueError
if this binding does not validate the
condition.
>>> Inhibitor(Expression('x+1'), Expression('x>0')).bind(Substitution(x=2))
()
>>> try : Inhibitor(Expression('x+1'), Expression('x<0')).bind(Substitution(x=2))
... except ValueError : print(sys.exc_info()[1])
condition not True for {x -> 2}
Call API
Substitution binding
: a substitutionreturn object
: empty tuple
Method Inhibitor.modes
def modes (self, values) :
Return the list of modes under which an arc with the
annotation may flow the tokens in values
. Each mode is a
substitution indicating how to bind the annotation.
>>> try : Inhibitor(Value(1)).modes([1, 2, 3])
... except ModeError : print(sys.exc_info()[1])
inhibited by {}
>>> Inhibitor(Value(0)).modes([1, 2, 3])
[Substitution()]
>>> try : Inhibitor(Variable('x')).modes([1, 2, 3])
... except ModeError : print(sys.exc_info()[1])
inhibited by {x -> 1}
>>> Inhibitor(Variable('x'), Expression('x>3')).modes([1, 2, 3])
[Substitution()]
>>> try : Inhibitor(MultiArc([Variable('x'), Variable('y')]),
... Expression('x>y')).modes([1, 2, 3])
... except ModeError : print(sys.exc_info()[1])
inhibited by {...}
>>> Inhibitor(MultiArc([Variable('x'), Variable('y')]),
... Expression('x==y')).modes([1, 2, 3])
[Substitution()]
Call API
collection values
: a collection of valuesreturn list
: a list of substitutions
Method Inhibitor.check
def check (self, binding, tokens) :
Check whether the label allows to fire with tokens
>>> Inhibitor(Value(1)).check(Substitution(), MultiSet([1, 2, 3]))
False
>>> Inhibitor(Value(4)).check(Substitution(), MultiSet([1, 2, 3]))
True
>>> Inhibitor(Variable("x")).check(Substitution(x=1), MultiSet([1, 2, 3]))
False
>>> Inhibitor(Variable("x")).check(Substitution(x=4), MultiSet([1, 2, 3]))
True
Class Flush
class Flush (ArcAnnotation) :
A flush arc used as on input arc will consume all the tokens in a place, binding this multiset to a variable. When used as an output arc, it will produce several tokens in the output place.
Method Flush.__init__
def __init__ (self, expr) :
Build a flush arc from either a variable name or an expression. In the latter case, the annotation is only allowed to be used on output arcs.
Method Flush.modes
def modes (self, values) :
Return the list of modes under which an arc with the
annotation may flow the tokens in values
. Each mode is a
substitution indicating how to bind the annotation. In the
case of flush arcs, there will be only one mode that binds all
the tokens in a multiset.
>>> m = Flush('x').modes([1, 2, 3])
>>> m
[Substitution(x=MultiSet([...]))]
>>> m[0]['x'] == MultiSet([1, 2, 3])
True
Take care that a flush arc allows to fire a transition by consuming no token in a place. This is different from usual arc annotation that require tokens to be present in input places.
>>> Flush('x').modes([])
[Substitution(x=MultiSet([]))]
Call API
collection values
: a collection of valuesreturn list
: a list of substitutions
Method Flush.flow
def flow (self, binding) :
Return the flow of tokens implied by the annotation evaluated
through binding
.
>>> Flush('x').flow(Substitution(x=MultiSet([1, 2, 3]))) == MultiSet([1, 2, 3])
True
Call API
Substitution binding
: a substitutionreturn object
: a multiset
Method Flush.bind
def bind (self, binding) :
Return the value of the annotation evaluated through
binding
.
>>> set(Flush('x').bind(Substitution(x=MultiSet([1, 2, 3])))) == set([Token(1), Token(2), Token(3)])
True
Call API
Substitution binding
: a substitutionreturn object
: a value
Petri net nodes
Class Node
class Node (NetElement) :
A node in a Petri net.
This class is abstract and should not be instanciated, use Place
or Transition
as concrete nodes.
Method Node.rename
def rename (self, name) :
Change the name of the node.
>>> p = Place('p', range(3))
>>> p.rename('egg')
>>> p
Place('egg', MultiSet([...]), tAll)
>>> t = Transition('t', Expression('x==1'))
>>> t.rename('spam')
>>> t
Transition('spam', Expression('x==1'))
Call API
str name
: the new name of the node
Class Place
class Place (Node) :
A place of a Petri net.
Method Place.__init__
def __init__ (self, name, tokens=[], check=None) :
Initialise with name, tokens and typecheck. tokens
may be
a single value or an iterable object. check
may be None
(any token allowed) or a type from module `snakes.typing.
>>> Place('p', range(3), tInteger)
Place('p', MultiSet([...]), Instance(int))
Call API
str name
: the name of the placecollection tokens
: a collection of tokens that mark the placeType check
: a constraint on the tokens allowed in the place (orNone
for no constraint)
Method Place.copy
def copy (self, name=None) :
Return a copy of the place, with no arc attached.
>>> p = Place('p', range(3), tInteger)
>>> n = p.copy()
>>> n.name == 'p'
True
>>> n.tokens == MultiSet([0, 1, 2])
True
>>> n.checker()
Instance(int)
>>> n = p.copy('x')
>>> n.name == 'x'
True
>>> n.tokens == MultiSet([0, 1, 2])
True
>>> n.checker()
Instance(int)
Call API
str name
: if notNone
, the name of the copyreturn Place
: a copy of the place.
Method Place.checker
def checker (self, check=None) :
Change or return the type of the place: if check
is
None
, current type is returned, otherwise, it is assigned
with the given value.
>>> p = Place('p', range(3), tInteger)
>>> p.checker()
Instance(int)
>>> p.checker(tAll)
>>> p.checker()
tAll
Call API
Type check
: the new constraint for the place orNone
to retreive the current constraintreturn Type
: the current constraint ifcheck
isNone
orNone
otherwise
Method Place.__contains__
def __contains__ (self, token) :
Check if a token is in the place.
>>> p = Place('p', range(3))
>>> 1 in p
True
>>> 5 in p
False
Call API
object token
: a token valuereturn bool
:True
iftoken
is held by the place,False
otherwise
Method Place.__iter__
def __iter__ (self) :
Iterate over the tokens in the place, including repetitions.
>>> p = Place('p', list(range(3))*2)
>>> list(sorted([tok for tok in p]))
[0, 0, 1, 1, 2, 2]
Call API
return object
: an iterator object
Method Place.is_empty
def is_empty (self) :
Check is the place is empty.
>>> p = Place('p', range(3))
>>> p.tokens == MultiSet([0, 1, 2])
True
>>> p.is_empty()
False
>>> p.tokens = MultiSet()
>>> p.is_empty()
True
Call API
return bool
:True
if the place is empty,False
otherwise
Method Place.check
def check (self, tokens) :
Check if the tokens
are allowed in the place. Exception
ValueError
is raised whenever a forbidden token is
encountered.
>>> p = Place('p', [], tInteger)
>>> p.check([1, 2, 3])
>>> try : p.check(['forbidden!'])
... except ValueError : print(sys.exc_info()[1])
forbidden token 'forbidden!'
Call API
collection tokens
: an iterable collection of tokens or a single value
Exceptions
ValueError
: when some of the checked tokens are not allowed in the place
Method Place.add
def add (self, tokens) :
Add tokens to the place.
>>> p = Place('p')
>>> p.tokens == MultiSet([])
True
>>> p.add(range(3))
>>> p.tokens == MultiSet([0, 1, 2])
True
Call API
collection tokens
: a collection of tokens to be added to the place, note thatstr
are not considered as iterable and used a a single value instead of as a collection
Method Place.remove
def remove (self, tokens) :
Remove tokens from the place.
>>> p = Place('p', list(range(3)) * 2)
>>> p.tokens == MultiSet([0, 0, 1, 1, 2, 2])
True
>>> p.remove(range(3))
>>> p.tokens == MultiSet([0, 1, 2])
True
Call API
collection tokens
: a collection of tokens to be removed from the place, note thatstr
are not considered as iterable and used a a single value instead of as a collection
Method Place.empty
def empty (self) :
Remove all the tokens.
>>> p = Place('p', list(range(3)) * 2)
>>> p.tokens == MultiSet([0, 0, 1, 1, 2, 2])
True
>>> p.empty()
>>> p.tokens == MultiSet([])
True
Method Place.reset
def reset (self, tokens) :
Replace the marking with tokens
.
>>> p = Place('p', list(range(3)) * 2)
>>> p.tokens == MultiSet([0, 0, 1, 1, 2, 2])
True
>>> p.reset(['a', 'b'])
>>> p.tokens == MultiSet(['a', 'b'])
True
Class Transition
class Transition (Node) :
A transition in a Petri net.
Method Transition.__init__
def __init__ (self, name, guard=None) :
Initialise with the name and the guard. If guard
is
None
, Expression('True')
is assumed instead:
>>> Transition('t').guard
Expression('True')
Call API
str name
: the name of the transitionExpression guard
: the guard of the transition
Method Transition.copy
def copy (self, name=None) :
Return a copy of the transition, with no arc attached.
>>> t = Transition('t', Expression('x==1'))
>>> t.copy()
Transition('t', Expression('x==1'))
>>> t.copy('x')
Transition('x', Expression('x==1'))
Call API
str name
: if notNone
, the name if of the copyreturn Transition
: a copy of the transition.
Method Transition.add_input
def add_input (self, place, label) :
Add an input arc from place
labelled by label
.
>>> t = Transition('t')
>>> t.add_input(Place('p'), Variable('x'))
>>> t.input()
[(Place('p', MultiSet([]), tAll), Variable('x'))]
>>> try : t.add_input(Place('x'), Expression('x+y'))
... except ConstraintError : print(sys.exc_info()[1])
'Expression' objects not allowed on input arcs
Call API
Place place
: the input placeArcAnnotation label
: the arc label
Exceptions
ConstraintError
: when an annotation is added on an input arcs where it is actually not allowed, or when there is already an arc from this place
Method Transition.remove_input
def remove_input (self, place) :
Remove the input arc from place
.
>>> t = Transition('t')
>>> p = Place('p')
>>> t.add_input(p, Variable('x'))
>>> t.input()
[(Place('p', MultiSet([]), tAll), Variable('x'))]
>>> t.remove_input(p)
>>> t.input()
[]
Call API
Place place
: the input place
Exceptions
ConstraintError
: when there is no arc from this place
Method Transition.input
def input (self) :
Return the list of input arcs.
>>> t = Transition('t')
>>> t.add_input(Place('p'), Variable('x'))
>>> t.input()
[(Place('p', MultiSet([]), tAll), Variable('x'))]
Call API
return list
: a list of pairs (place, label).
Method Transition.add_output
def add_output (self, place, label) :
Add an output arc from place
labelled by label
.
>>> t = Transition('t')
>>> t.add_output(Place('p'), Expression('x+1'))
>>> t.output()
[(Place('p', MultiSet([]), tAll), Expression('x+1'))]
Call API
Place place
: the output placeArcAnnotation label
: the arc label
Exceptions
ConstraintError
: when there is already an arc to this place
Method Transition.remove_output
def remove_output (self, place) :
Remove the output arc to place
.
>>> t = Transition('t')
>>> p = Place('p')
>>> t.add_output(p, Variable('x'))
>>> t.output()
[(Place('p', MultiSet([]), tAll), Variable('x'))]
>>> t.remove_output(p)
>>> t.output()
[]
Call API
Place place
: the output place
Exceptions
ConstraintError
: when there is no arc to this place
Method Transition.output
def output (self) :
Return the list of output arcs.
>>> t = Transition('t')
>>> t.add_output(Place('p'), Expression('x+1'))
>>> t.output()
[(Place('p', MultiSet([]), tAll), Expression('x+1'))]
Call API
return list
: a list of pairs (place, label).
Method Transition.activated
def activated (self, binding) :
Check if binding
activates the transition. This is the
case if the guard evaluates to True
and if the types of the
places are respected. Note that the presence of enough tokens
is not required.
>>> t = Transition('t', Expression('x>0'))
>>> p = Place('p', [], tInteger)
>>> t.add_input(p, Variable('x'))
>>> t.activated(Substitution(x=1))
True
>>> t.activated(Substitution(x=-1))
False
>>> t.activated(Substitution(x=3.14))
False
Call API
Substitution binding
: a valuation of the variables on the transition
Method Transition.enabled
def enabled (self, binding) :
Check if binding
enables the transition. This is the case
if the transition is activated and if the input places hold
enough tokens to allow the firing.
>>> t = Transition('t', Expression('x>0'))
>>> p = Place('p', [0], tInteger)
>>> t.add_input(p, Variable('x'))
>>> t.enabled(Substitution(x=1))
False
>>> p.add(1)
>>> t.enabled(Substitution(x=1))
True
Call API
Substitution binding
: a valuation of the variables on the transition
Method Transition.vars
def vars (self) :
Return the set of variables involved in the guard, input and output arcs of the transition.
>>> t = Transition('t', Expression('z is not None'))
>>> px = Place('px')
>>> t.add_input(px, Variable('x'))
>>> py = Place('py')
>>> t.add_output(py, Variable('y'))
>>> list(sorted(t.vars()))
['x', 'y', 'z']
Call API
return set
: the set of variables names
Method Transition.substitute
def substitute (self, binding) :
Substitute all the annotations arround the transition.
binding
is used to substitute the guard and all the labels
on the arcs attached to the transition.
>>> t = Transition('t', Expression('z is not None'))
>>> px = Place('px')
>>> t.add_input(px, Variable('x'))
>>> py = Place('py')
>>> t.add_output(py, Variable('y'))
>>> t.substitute(Substitution(x='a', y='b', z='c'))
>>> t
Transition('t', Expression('(c is not None)'))
>>> t.input()
[(Place('px', MultiSet([]), tAll), Variable('a'))]
>>> t.output()
[(Place('py', MultiSet([]), tAll), Variable('b'))]
Call API
Substitution binding
: a substitution from variables to variables (not values)
Method Transition.modes
def modes (self) :
Return the list of bindings which enable the transition. Note that the modes are usually considered to be the list of bindings that activate a transitions. However, this list may be infinite so we retricted ourselves to actual modes, taking into account only the tokens actually present in the input places.
>>> t = Transition('t', Expression('x!=y'))
>>> px = Place('px', range(2))
>>> t.add_input(px, Variable('x'))
>>> py = Place('py', range(2))
>>> t.add_input(py, Variable('y'))
>>> m = t.modes()
>>> len(m)
2
>>> Substitution(y=0, x=1) in m
True
>>> Substitution(y=1, x=0) in m
True
Note also that modes cannot be computed with respect to output arcs: indeed, only input arcs allow for concretely associate values to variables; on the other hand, binding an output arc would require to solve the equation provided by the guard.
>>> t = Transition('t', Expression('x!=y'))
>>> px = Place('px', range(2))
>>> t.add_input(px, Variable('x'))
>>> py = Place('py')
>>> t.add_output(py, Variable('y'))
>>> t.modes()
Traceback (most recent call last):
...
NameError: name 'y' is not defined
Call API
return list
: a list of substitutions usable to fire the transition
Method Transition.flow
def flow (self, binding) :
Return the token flow for a firing with binding
. The flow
is represented by a pair (in, out)
, both being instances of
the class Marking
.
>>> t = Transition('t', Expression('x!=1'))
>>> px = Place('px', range(3))
>>> t.add_input(px, Variable('x'))
>>> py = Place('py')
>>> t.add_output(py, Expression('x+1'))
>>> t.flow(Substitution(x=0))
(Marking({'px': MultiSet([0])}), Marking({'py': MultiSet([1])}))
>>> try : t.flow(Substitution(x=1))
... except ValueError : print(sys.exc_info()[1])
transition not enabled for {x -> 1}
>>> t.flow(Substitution(x=2))
(Marking({'px': MultiSet([2])}), Marking({'py': MultiSet([3])}))
Call API
Substitution binding
: a substitution from variables to values (not variables)return tuple
: a pair of marking to be respectively consumed or produced by the firing of the transition withbinding
Exceptions
ValueError
: when the provided binding does not enable the transition
Method Transition.fire
def fire (self, binding) :
Fire the transition with binding
.
>>> t = Transition('t', Expression('x!=1'))
>>> px = Place('px', range(3))
>>> t.add_input(px, Variable('x'))
>>> py = Place('py')
>>> t.add_output(py, Expression('x+1'))
>>> t.fire(Substitution(x=0))
>>> px.tokens == MultiSet([1, 2])
True
>>> py.tokens == MultiSet([1])
True
>>> try : t.fire(Substitution(x=1))
... except ValueError : print(sys.exc_info()[1])
transition not enabled for {x -> 1}
>>> t.fire(Substitution(x=2))
>>> px.tokens == MultiSet([1])
True
>>> py.tokens == MultiSet([1, 3])
True
Call API
Substitution binding
: a substitution from variables to values (not variables)
Exceptions
ValueError
: when the provided binding does not enable the transition
Marking, Petri nets and state graphs
Class Marking
class Marking (hdict) :
A marking of a Petri net. This is basically a
snakes.hashables.hdict
mapping place names to multisets of
tokens.
The parameters for the constructor must be given in a form
suitable for initialising a hdict
with place names as keys and
multisets as values. Places not given in the marking are assumed
empty. A Marking
object is independent of any Petri net and so
its list of places is not related to the places actually present
in a given net. This allows in particular to extract a Marking
from one Petri net and to assign it to another.
Method Marking.__str__
def __str__ (self) :
Method Marking.__call__
def __call__ (self, place) :
Return the marking of place
. The empty multiset is
returned if place
is not explicitely given in the marking.
>>> m = Marking(p1=MultiSet([1]), p2=MultiSet([2]))
>>> m('p1')
MultiSet([1])
>>> m('p')
MultiSet([])
Call API
str place
: a place namereturn MultiSet
: the marking ofplace
Method Marking.copy
def copy (self) :
Copy a marking
>>> m = Marking(p1=MultiSet([1]), p2=MultiSet([2]))
>>> m.copy() == Marking({'p2': MultiSet([2]), 'p1': MultiSet([1])})
True
Method Marking.__add__
def __add__ (self, other) :
Addition of markings.
>>> Marking(p1=MultiSet([1]), p2=MultiSet([2])) + Marking(p2=MultiSet([2]), p3=MultiSet([3])) == Marking({'p2': MultiSet([2, 2]), 'p3': MultiSet([3]), 'p1': MultiSet([1])})
True
Call API
Marking other
: another markingreturn Marking
: the addition of the two markings
Method Marking.__sub__
def __sub__ (self, other) :
Substraction of markings.
>>> Marking(p1=MultiSet([1]), p2=MultiSet([2, 2])) - Marking(p2=MultiSet([2]))
Marking({'p2': MultiSet([2]), 'p1': MultiSet([1])})
>>> try : Marking(p1=MultiSet([1]), p2=MultiSet([2])) - Marking(p2=MultiSet([2, 2]))
... except ValueError : print(sys.exc_info()[1])
not enough occurrences
>>> Marking(p1=MultiSet([1]), p2=MultiSet([2])) - Marking(p3=MultiSet([3]))
Traceback (most recent call last):
...
DomainError: 'p3' absent from the marking
Call API
Marking other
: another markingreturn Marking
: the difference of the two markings
Method Marking.__ge__
def __ge__ (self, other) :
Test if the marking self
is greater than or equal to
other
. This is the case when any place in other
is also in
self
and is marked with a smaller or equal multiset of
tokens.
>>> Marking(p=MultiSet([1])) >= Marking(p=MultiSet([1]))
True
>>> Marking(p=MultiSet([1, 1])) >= Marking(p=MultiSet([1]))
True
>>> Marking(p=MultiSet([1]), r=MultiSet([2])) >= Marking(p=MultiSet([1]))
True
>>> Marking(p=MultiSet([1])) >= Marking(p=MultiSet([1, 2]))
False
>>> Marking(p=MultiSet([1]), r=MultiSet([2])) >= Marking(p=MultiSet([1, 1]))
False
Call API
Marking other
: another markingreturn bool
:True
ifself >= other
,False
otherwise
Method Marking.__gt__
def __gt__ (self, other) :
Test if the marking self
is strictly greater than
other
. This is the case when any place in other
is also in
self
and either one place in other
is marked with a
smaller multiset of tokens or slef
has more places than
other
.
>>> Marking(p=MultiSet([1])) > Marking(p=MultiSet([1]))
False
>>> Marking(p=MultiSet([1, 1])) > Marking(p=MultiSet([1]))
True
>>> Marking(p=MultiSet([1]), r=MultiSet([2])) > Marking(p=MultiSet([1]))
True
>>> Marking(p=MultiSet([1])) > Marking(p=MultiSet([1, 2]))
False
>>> Marking(p=MultiSet([1]), r=MultiSet([2])) > Marking(p=MultiSet([1, 1]))
False
Call API
Marking other
: another markingreturn bool
:True
ifself > other
,False
otherwise
Method Marking.__le__
def __le__ (self, other) :
Test if the marking self
is smaller than or equal to
other
. This is the case when any place in self
is also in
other
and is marked with a smaller or equal multiset of
tokens.
>>> Marking(p=MultiSet([1])) <= Marking(p=MultiSet([1]))
True
>>> Marking(p=MultiSet([1])) <= Marking(p=MultiSet([1, 1]))
True
>>> Marking(p=MultiSet([1])) <= Marking(p=MultiSet([1]), r=MultiSet([2]))
True
>>> Marking(p=MultiSet([1, 2])) <= Marking(p=MultiSet([1]))
False
>>> Marking(p=MultiSet([1, 1])) <= Marking(p=MultiSet([1]), r=MultiSet([2]))
False
Call API
Marking other
: another markingreturn bool
:True
ifself <= other
,False
otherwise
Method Marking.__lt__
def __lt__ (self, other) :
Test if the marking self
is strictly smaller than
other
. This is the case when any place in self
is also in
other
and either one place in self
marked in self with a
strictly smaller multiset of tokens or other
has more places
than self
.
>>> Marking(p=MultiSet([1])) < Marking(p=MultiSet([1]))
False
>>> Marking(p=MultiSet([1])) < Marking(p=MultiSet([1, 1]))
True
>>> Marking(p=MultiSet([1])) < Marking(p=MultiSet([1]), r=MultiSet([2]))
True
>>> Marking(p=MultiSet([1, 2])) < Marking(p=MultiSet([1]))
False
>>> Marking(p=MultiSet([1, 1])) < Marking(p=MultiSet([1]), r=MultiSet([2]))
False
Call API
Marking other
: another markingreturn bool
:True
ifself < other
,False
otherwise
Class PetriNet
class PetriNet (object) :
A Petri net. As soon as nodes are added to a PetriNet
, they
should be handled by name instead of by the Place
or
Transition
instance. For instance:
>>> n = PetriNet('N')
>>> t = Transition('t')
>>> n.add_transition(t)
>>> n.has_transition('t') # use 't' and not t
True
>>> n.transition('t') is t
True
Method PetriNet.__init__
def __init__ (self, name) :
Initialise with a name that may be an arbitrary string.
>>> PetriNet('N')
PetriNet('N')
Call API
str name
: the name of the net
Method PetriNet.copy
def copy (self, name=None) :
Return a complete copy of the net, including places, transitions, arcs and declarations.
>>> PetriNet('N').copy()
PetriNet('N')
>>> PetriNet('N').copy('x')
PetriNet('x')
Call API
str name
: if notNone
, the name of the copyreturn PetriNet
: a copy of the net
Method PetriNet.rename
def rename (self, name) :
Change the name of the net.
>>> n = PetriNet('N')
>>> n.rename('Long name!')
>>> n
PetriNet('Long name!')
Call API
str name
: the new name
Method PetriNet.has_place
def has_place (self, name) :
Check if there is a place called name
in the net.
>>> n = PetriNet('N')
>>> n.has_place('p')
False
>>> n.add_place(Place('p'))
>>> n.has_place('p')
True
Call API
str name
: the name of the searched placereturn bool
:True
if a place calledname
is present in the net,False
otherwise
Method PetriNet.has_transition
def has_transition (self, name) :
Check if there is a transition called name
in the net.
>>> n = PetriNet('N')
>>> n.has_transition('t')
False
>>> n.add_transition(Transition('t'))
>>> n.has_transition('t')
True
Call API
str name
: the name of the searched transitionreturn bool
:True
if a transition calledname
is present in the net,False
otherwise
Method PetriNet.has_node
def has_node (self, name) :
Check if there is a transition called name
in the net.
>>> n = PetriNet('N')
>>> n.has_node('t')
False
>>> n.has_node('p')
False
>>> n.add_transition(Transition('t'))
>>> n.add_place(Place('p'))
>>> n.has_node('t')
True
>>> n.has_node('p')
True
Call API
str name
: the name of the searched nodereturn bool
:True
if a node calledname
is present in the net,False
otherwise
Method PetriNet.__contains__
def __contains__ (self, name) :
name in net
is a shortcut for net.has_node(name)
>>> n = PetriNet('N')
>>> 't' in n
False
>>> 'p' in n
False
>>> n.add_transition(Transition('t'))
>>> n.add_place(Place('p'))
>>> 't' in n
True
>>> 'p' in n
True
Call API
str name
: the name of the searched nodereturn bool
:True
if a node calledname
is present in the net,False
otherwise
Method PetriNet.declare
def declare (self, statements, locals=None) :
Execute statements
in the global dictionnary of the net.
This has also on the dictionnarie of the instances of
Expression
in the net (guards of the transitions and labels
on the arcs) so the declarations have an influence over the
elements embedded in the net. If locals
is given, most of
the declared objects will be placed in it instead of the
global dictionnary, see the documentation of Python for more
details about local and global environments.
>>> n = PetriNet('N')
>>> t = Transition('t', Expression('x==0'))
>>> n.add_transition(t)
>>> t.guard(Substitution())
Traceback (most recent call last):
...
NameError: name 'x' is not defined
>>> n.declare('x=0')
>>> t.guard(Substitution())
True
>>> n.add_place(Place('p'))
>>> n.add_output('p', 't', Expression('math.pi'))
>>> t.fire(Substitution())
Traceback (most recent call last):
...
NameError: name 'math' is not defined
>>> n.declare('import math')
>>> t.fire(Substitution())
>>> n.place('p')
Place('p', MultiSet([3.14...]), tAll)
Call API
str statements
: a Python instruction suitable toexec
dict locals
: adict
used as locals whenstatements
is executed, orNone
Method PetriNet.add_place
def add_place (self, place) :
Add a place to the net. Each node in a net must have a name unique to this net, which is checked when it is added.
>>> n = PetriNet('N')
>>> n.place('p')
Traceback (most recent call last):
...
ConstraintError: place 'p' not found
>>> n.add_place(Place('p', range(3)))
>>> n.place('p')
Place('p', MultiSet([...]), tAll)
>>> n.place('p').tokens == MultiSet([0, 1, 2])
True
>>> n.add_place(Place('p'))
Traceback (most recent call last):
...
ConstraintError: place 'p' exists
Call API
Place place
: the place to add
Exceptions
ConstraintError
: when a place with the same name exists already in the net
Method PetriNet.remove_place
def remove_place (self, name) :
Remove a place (given by its name) from the net.
>>> n = PetriNet('N')
>>> n.remove_place('p')
Traceback (most recent call last):
...
ConstraintError: place 'p' not found
>>> n.add_place(Place('p', range(3)))
>>> n.place('p')
Place('p', MultiSet([...]), tAll)
>>> n.remove_place('p')
>>> n.place('p')
Traceback (most recent call last):
...
ConstraintError: place 'p' not found
Call API
str name
: the name of the place to remove
Exceptions
ConstraintError
: when no place with this name exists in the net
Method PetriNet.add_transition
def add_transition (self, trans) :
Add a transition to the net. Each node in a net must have a name unique to this net, which is checked when it is added.
>>> n = PetriNet('N')
>>> n.transition('t')
Traceback (most recent call last):
...
ConstraintError: transition 't' not found
>>> n.add_transition(Transition('t', Expression('x==1')))
>>> n.transition('t')
Transition('t', Expression('x==1'))
>>> n.add_transition(Transition('t'))
Traceback (most recent call last):
...
ConstraintError: transition 't' exists
Call API
Transition trans
: the transition to add
Exceptions
ConstraintError
: when a transition with the same name exists already in the net
Method PetriNet.remove_transition
def remove_transition (self, name) :
Remove a transition (given by its name) from the net.
>>> n = PetriNet('N')
>>> n.remove_transition('t')
Traceback (most recent call last):
...
ConstraintError: transition 't' not found
>>> n.add_transition(Transition('t', Expression('x==1')))
>>> n.transition('t')
Transition('t', Expression('x==1'))
>>> n.remove_transition('t')
>>> n.transition('t')
Traceback (most recent call last):
...
ConstraintError: transition 't' not found
Call API
str name
: the name of the transition to remove
Exceptions
ConstraintError
: when no transition with this name exists in the net
Method PetriNet.place
def place (self, name=None) :
Return one (if name
is not None
) or all the places.
>>> n = PetriNet('N')
>>> n.add_place(Place('p1'))
>>> n.add_place(Place('p2'))
>>> n.place('p1')
Place('p1', MultiSet([]), tAll)
>>> n.place('p')
Traceback (most recent call last):
...
ConstraintError: place 'p' not found
>>> n.place()
[Place('p2', MultiSet([]), tAll), Place('p1', MultiSet([]), tAll)]
Call API
str name
: the name of the place to retrieve orNone
to get the list of all the places in the netreturn object
: a place whose name isname
or a list of places
Exceptions
ConstraintError
: when a place requested does not exist
Method PetriNet.transition
def transition (self, name=None) :
Return one (if name
is not None
) or all the transitions.
>>> n = PetriNet('N')
>>> n.add_transition(Transition('t1'))
>>> n.add_transition(Transition('t2'))
>>> n.transition('t1')
Transition('t1', Expression('True'))
>>> n.transition('t')
Traceback (most recent call last):
...
ConstraintError: transition 't' not found
>>> n.transition()
[Transition('t2', Expression('True')),
Transition('t1', Expression('True'))]
Call API
str name
: the name of the transition to retrieve orNone
to get the list of all the transitions in the netreturn object
: a transition whose name isname
or a list of transitions
Exceptions
ConstraintError
: when a place requested does not exist
Method PetriNet.node
def node (self, name=None) :
Return one (if name
is not None
) or all the nodes.
>>> n = PetriNet('N')
>>> n.add_transition(Transition('t'))
>>> n.add_place(Place('p'))
>>> n.node('t')
Transition('t', Expression('True'))
>>> n.node('x')
Traceback (most recent call last):
...
ConstraintError: node 'x' not found
>>> list(sorted(n.node(), key=str))
[Place('p', MultiSet([]), tAll), Transition('t', Expression('True'))]
Call API
str name
: the name of the node to retrieve orNone
to get the list of all the nodes in the netreturn object
: a node whose name isname
or a list of nodes
Exceptions
ConstraintError
: when a node requested does not exist
Method PetriNet.add_input
def add_input (self, place, trans, label) :
Add an input arc between place
and trans
(nodes names).
An input arc is directed from a place toward a transition.
>>> n = PetriNet('N')
>>> n.add_place(Place('p', range(3)))
>>> n.add_transition(Transition('t', Expression('x!=1')))
>>> try : n.add_input('p', 't', Expression('2*x'))
... except ConstraintError : print(sys.exc_info()[1])
'Expression' not allowed on input arcs
>>> n.add_input('p', 't', Variable('x'))
>>> (n.post('p'), n.pre('t')) == (set(['t']), set(['p']))
True
>>> n.transition('t').modes()
[Substitution(x=0), Substitution(x=2)]
>>> n.place('p').tokens == MultiSet([0, 1, 2])
True
>>> n.transition('t').fire(Substitution(x=0))
>>> n.place('p').tokens == MultiSet([1, 2])
True
>>> try : n.add_input('p', 't', Value(42))
... except ConstraintError: print(sys.exc_info()[1])
already connected to 'p'
Call API
str place
: the name of the place to connectstr trans
: the name of the transition to connectArcAnnotation label
: the annotation of the arc
Exceptions
ConstraintError
: in case of anything not allowed
Method PetriNet.remove_input
def remove_input (self, place, trans) :
Remove an input arc between place
and trans
(nodes names).
>>> n = PetriNet('N')
>>> n.add_place(Place('p', range(3)))
>>> n.add_transition(Transition('t', Expression('x!=1')))
>>> n.add_input('p', 't', Variable('x'))
>>> (n.post('p'), n.pre('t')) == (set(['t']), set(['p']))
True
>>> n.remove_input('p', 't')
>>> (n.post('p'), n.pre('t')) == (set([]), set([]))
True
>>> try : n.remove_input('p', 't')
... except ConstraintError : print(sys.exc_info()[1])
not connected to 'p'
Call API
str place
: the name of the place to disconnectstr trans
: the name of the transition to disconnect
Exceptions
ConstraintError
: when this arc does not exist
Method PetriNet.add_output
def add_output (self, place, trans, label) :
Add an output arc between place
and trans
(nodes names).
An output arc is directed from a transition toward a place.
>>> n = PetriNet('N')
>>> n.add_place(Place('p'))
>>> n.add_transition(Transition('t'))
>>> n.add_output('p', 't', Value(42))
>>> (n.post('t'), n.pre('p')) == (set(['p']), set(['t']))
True
>>> n.place('p').tokens == MultiSet([])
True
>>> n.transition('t').fire(Substitution())
>>> n.place('p').tokens == MultiSet([42])
True
>>> try : n.add_output('p', 't', Value(42))
... except ConstraintError : print(sys.exc_info()[1])
already connected to 'p'
Call API
str place
: the name of the place to connectstr trans
: the name of the transition to connectArcAnnotation label
: the annotation of the arc
Exceptions
ConstraintError
: in case of anything not allowed
Method PetriNet.remove_output
def remove_output (self, place, trans) :
Remove an output arc between place
and trans
(nodes
names).
>>> n = PetriNet('N')
>>> n.add_place(Place('p'))
>>> n.add_transition(Transition('t'))
>>> n.add_output('p', 't', Value(42))
>>> (n.post('t'), n.pre('p')) == (set(['p']), set(['t']))
True
>>> n.remove_output('p', 't')
>>> (n.post('t'), n.pre('p')) == (set([]), set([]))
True
>>> try : n.remove_output('p', 't')
... except ConstraintError : print(sys.exc_info()[1])
not connected to 'p'
Call API
str place
: the name of the place to disconnectstr trans
: the name of the transition to disconnect
Exceptions
ConstraintError
: when this arc does not exist
Method PetriNet.pre
def pre (self, nodes) :
Return the set of nodes names preceeding nodes
. nodes
can be a single node name ot a list of nodes names.
>>> n = PetriNet('N')
>>> n.add_place(Place('p1'))
>>> n.add_place(Place('p2'))
>>> n.add_transition(Transition('t1'))
>>> n.add_transition(Transition('t2'))
>>> n.add_output('p1', 't1', Value(1))
>>> n.add_output('p2', 't2', Value(2))
>>> n.pre('p1') == set(['t1'])
True
>>> n.pre(['p1', 'p2']) == set(['t2', 't1'])
True
Call API
list nodes
: a single node name or a list of node namesreturn set
: a set of node names
Method PetriNet.post
def post (self, nodes) :
Return the set of nodes names succeeding nodes
. nodes
can be a single node name ot a list of nodes names.
>>> n = PetriNet('N')
>>> n.add_place(Place('p1'))
>>> n.add_place(Place('p2'))
>>> n.add_transition(Transition('t1'))
>>> n.add_transition(Transition('t2'))
>>> n.add_output('p1', 't1', Value(1))
>>> n.add_output('p2', 't2', Value(2))
>>> n.post('t1') == set(['p1'])
True
>>> n.post(['t1', 't2']) == set(['p2', 'p1'])
True
Call API
list nodes
: a single node name or a list of node namesreturn set
: a set of node names
Method PetriNet.get_marking
def get_marking (self) :
Return the current marking of the net, omitting empty places.
>>> n = PetriNet('N')
>>> n.add_place(Place('p0', range(0)))
>>> n.add_place(Place('p1', range(1)))
>>> n.add_place(Place('p2', range(2)))
>>> n.add_place(Place('p3', range(3)))
>>> n.get_marking() == Marking({'p2': MultiSet([0, 1]), 'p3': MultiSet([0, 1, 2]), 'p1': MultiSet([0])})
True
Call API
return Marking
: the current marking
Method PetriNet.set_marking
def set_marking (self, marking) :
Assign a marking to the net. Places not listed in the marking are considered empty, the corresponding place in the net is thus emptied. If the marking has places that do not belong to the net, these are ignored (as in the last instruction below). If an error occurs during the assignment, the marking is left unchanged.
>>> n = PetriNet('N')
>>> n.add_place(Place('p0', range(5), tInteger))
>>> n.add_place(Place('p1'))
>>> n.add_place(Place('p2'))
>>> n.add_place(Place('p3'))
>>> n.get_marking() == Marking({'p0': MultiSet([0, 1, 2, 3, 4])})
True
>>> n.set_marking(Marking(p1=MultiSet([0]), p2=MultiSet([0, 1]), p3=MultiSet([0, 1, 2])))
>>> n.get_marking() == Marking({'p2': MultiSet([0, 1]), 'p3': MultiSet([0, 1, 2]), 'p1': MultiSet([0])})
True
>>> n.set_marking(Marking(p=MultiSet([0])))
>>> n.get_marking()
Marking({})
>>> try : n.set_marking(Marking(p2=MultiSet([1]), p0=MultiSet([3.14])))
... except ValueError : print(sys.exc_info()[1])
forbidden token '3.14'
>>> n.get_marking() # unchanged
Marking({})
Call API
Marking marking
: the new marking
Method PetriNet.add_marking
def add_marking (self, marking) :
Add a marking to the current one. If an error occurs during the process, the marking is left unchanged. Places in the marking that do not belong to the net are ignored.
>>> n = PetriNet('N')
>>> n.add_place(Place('p1'))
>>> n.add_place(Place('p2', range(3)))
>>> n.get_marking() == Marking({'p2': MultiSet([0, 1, 2])})
True
>>> n.add_marking(Marking(p1=MultiSet(range(2)), p2=MultiSet([1])))
>>> n.get_marking() == Marking({'p2': MultiSet([0, 1, 1, 2]), 'p1': MultiSet([0, 1])})
True
Call API
Marking marking
: the new marking
Method PetriNet.remove_marking
def remove_marking (self, marking) :
Substract a marking from the current one. If an error occurs during the process, the marking is left unchanged. Places in the marking that do not belong to the net are ignored.
>>> n = PetriNet('N')
>>> n.add_place(Place('p1'))
>>> n.add_place(Place('p2', range(3)))
>>> n.get_marking() == Marking({'p2': MultiSet([0, 1, 2])})
True
>>> try : n.remove_marking(Marking(p1=MultiSet(range(2)), p2=MultiSet([1])))
... except ValueError : print(sys.exc_info()[1])
not enough occurrences
>>> n.get_marking() == Marking({'p2': MultiSet([0, 1, 2])})
True
>>> n.remove_marking(Marking(p2=MultiSet([1])))
>>> n.get_marking() == Marking({'p2': MultiSet([0, 2])})
True
Call API
Marking marking
: the new marking
Method PetriNet.rename_node
def rename_node (self, old, new) :
Change the name of a node.
>>> n = PetriNet('N')
>>> n.add_place(Place('p'))
>>> n.add_transition(Transition('t'))
>>> n.add_output('p', 't', Value(0))
>>> list(sorted(n.node(), key=str))
[Place('p', MultiSet([]), tAll), Transition('t', Expression('True'))]
>>> n.post('t') == set(['p'])
True
>>> n.rename_node('p', 'new_p')
>>> list(sorted(n.node(), key=str))
[Place('new_p', MultiSet([]), tAll), Transition('t', Expression('True'))]
>>> n.post('t') == set(['new_p'])
True
>>> try : n.rename_node('new_p', 't')
... except ConstraintError : print(sys.exc_info()[1])
node 't' exists
>>> try : n.rename_node('old_t', 'new_t')
... except ConstraintError : print(sys.exc_info()[1])
node 'old_t' not found
Call API
str old
: the current name of the nodestr new
: the new name for the node
Method PetriNet.copy_place
def copy_place (self, source, targets) :
Make copies of the source
place (use place names).
>>> n = PetriNet('N')
>>> n.add_place(Place('p', range(3)))
>>> n.add_transition(Transition('t'))
>>> n.add_input('p', 't', Value(0))
>>> n.copy_place('p', ['bis', 'ter'])
>>> n.copy_place('bis', 'more')
>>> list(sorted(n.place(), key=str))
[Place('bis', MultiSet([...]), tAll),
Place('more', MultiSet([...]), tAll),
Place('p', MultiSet([...]), tAll),
Place('ter', MultiSet([...]), tAll)]
>>> list(sorted(n.pre('t'), key=str))
['bis', 'more', 'p', 'ter']
Call API
str source
: the name of the place to copystr targets
: a name or a list of names for the copie(s)
Method PetriNet.copy_transition
def copy_transition (self, source, targets) :
Make copies of the source
transition (use transition names).
>>> n = PetriNet('N')
>>> n.add_transition(Transition('t', Expression('x==1')))
>>> n.add_place(Place('p'))
>>> n.add_input('p', 't', Value(0))
>>> n.copy_transition('t', ['bis', 'ter'])
>>> n.copy_transition('bis', 'more')
>>> list(sorted(n.transition(), key=str))
[Transition('bis', Expression('x==1')),
Transition('more', Expression('x==1')),
Transition('t', Expression('x==1')),
Transition('ter', Expression('x==1'))]
>>> list(sorted(n.post('p')))
['bis', 'more', 't', 'ter']
Call API
str source
: the name of the transition to copystr targets
: a name or a list of names for the copie(s)
Method PetriNet.merge_places
def merge_places (self, target, sources) :
Create a new place by merging those in sources
. Markings
are added, place types are 'or'ed and arcs labels are joinded
into multi-arcs, the sources places are not removed. Use
places names.
>>> n = PetriNet('n')
>>> n.add_place(Place('p1', [1], tInteger))
>>> n.add_place(Place('p2', [2.0], tFloat))
>>> n.add_transition(Transition('t1'))
>>> n.add_transition(Transition('t2'))
>>> n.add_output('p1', 't1', Value(1))
>>> n.add_output('p2', 't2', Value(2.0))
>>> n.add_output('p2', 't1', Value(2.0))
>>> n.merge_places('p', ['p1', 'p2'])
>>> (n.pre('p'), n.post('t1')) == (set(['t2', 't1']), set(['p2', 'p', 'p1']))
True
>>> list(sorted(n.node('p').pre.items()))
[('t1', MultiArc((Value(1), Value(2.0)))),
('t2', Value(2.0))]
>>> n.node('p').tokens == MultiSet([1, 2.0])
True
>>> n.node('p').checker()
(Instance(int) | Instance(float))
Call API
str target
: the name of the created placelist sources
: the list of places names to be merged (or a single place name)
Method PetriNet.merge_transitions
def merge_transitions (self, target, sources) :
Create a new transition by merging those in sources
.
Guards are 'and'ed and arcs labels are joinded into multi-
arcs, the sources transitions are not removed. Use transitions
names.
>>> n = PetriNet('n')
>>> n.add_place(Place('p1'))
>>> n.add_place(Place('p2'))
>>> n.add_transition(Transition('t1', Expression('x==1')))
>>> n.add_transition(Transition('t2', Expression('y==2')))
>>> n.add_output('p1', 't1', Value(1))
>>> n.add_output('p2', 't2', Value(2.0))
>>> n.add_output('p2', 't1', Value(2.0))
>>> n.merge_transitions('t', ['t1', 't2'])
>>> list(sorted(n.post('t'), key=str))
['p1', 'p2']
>>> list(sorted(n.pre('p2'), key=str))
['t', 't1', 't2']
>>> n.transition('t')
Transition('t', Expression('(x==1) and (y==2)'))
>>> n.node('t').post
{'p2': MultiArc((Value(2.0), Value(2.0))), 'p1': Value(1)}
Call API
str target
: the name of the created transitionlist sources
: the list of transitions names to be merged (or a single transition name)
Class StateGraph
class StateGraph (object) :
The graph of reachable markings of a net.
Method StateGraph.__init__
def __init__ (self, net) :
Initialise with the net.
>>> StateGraph(PetriNet('N')).net
PetriNet('N')
Call API
PetriNet net
: the Petri net whose graph has to be computed
Method StateGraph.goto
def goto (self, state) :
Change the current state to another (given by its number). This also changes the marking of the net consistently. Notice that the state may not exist yet.
>>> n = PetriNet('N')
>>> n.add_place(Place('p', [0]))
>>> n.add_transition(Transition('t', Expression('x<5')))
>>> n.add_input('p', 't', Variable('x'))
>>> n.add_output('p', 't', Expression('x+1'))
>>> g = StateGraph(n)
>>> try : g.goto(2)
... except ValueError : print(sys.exc_info()[1])
unknown state
>>> g.build()
>>> g.goto(2)
>>> g.net.get_marking()
Marking({'p': MultiSet([2])})
Call API
non-negative
int state`: the number of the state to go to
Method StateGraph.current
def current (self) :
Return the number of the current state.
>>> n = PetriNet('N')
>>> n.add_place(Place('p', [0]))
>>> n.add_transition(Transition('t', Expression('x<5')))
>>> n.add_input('p', 't', Variable('x'))
>>> n.add_output('p', 't', Expression('x+1'))
>>> g = StateGraph(n)
>>> g.current()
0
Call API
return non-negative
int`: the number of the current state
Method StateGraph.__getitem__
def __getitem__ (self, state) :
Return a marking by its state number.
Method StateGraph.__contains__
def __contains__ (self, marking) :
Method StateGraph.successors
def successors (self, state=None) :
Return the successors of the current state. The value returned is a
iterator over triples (succ, trans, mode)
representing the
number of the successor, the name of the transition and the
binding needed to reach the new state.
>>> n = PetriNet('N')
>>> n.add_place(Place('p', [0]))
>>> n.add_transition(Transition('t', Expression('x<5')))
>>> n.add_input('p', 't', Variable('x'))
>>> n.add_output('p', 't', Expression('x+1'))
>>> g = StateGraph(n)
>>> g.build()
>>> g.goto(2)
>>> list(g.successors())
[(3, Transition('t', Expression('x<5')), Substitution(x=2))]
Call API
object state
return generator of
(int, str, Substitution)`: iterator of triples successor/transition/mode to them
Method StateGraph.predecessors
def predecessors (self, state=None) :
Return the predecessors states. The returned value is as in
successors
. Notice that if the graph is not complete, this
value may be wrong: states computed in the future may lead to
the current one thus becoming one of its predecessors.
>>> n = PetriNet('N')
>>> n.add_place(Place('p', [0]))
>>> n.add_transition(Transition('t', Expression('x<5')))
>>> n.add_input('p', 't', Variable('x'))
>>> n.add_output('p', 't', Expression('x+1'))
>>> g = StateGraph(n)
>>> g.build()
>>> g.goto(2)
>>> list(g.predecessors())
[(1, Transition('t', Expression('x<5')), Substitution(x=1))]
Call API
object state
return generator of
(int, str, Substitution)`: iterator of triples predecessor/transition/mode to them
Method StateGraph.__len__
def __len__ (self) :
Return the number of states currently reached.
>>> n = PetriNet('N')
>>> n.add_place(Place('p', [0]))
>>> n.add_transition(Transition('t', Expression('x<5')))
>>> n.add_input('p', 't', Variable('x'))
>>> n.add_output('p', 't', Expression('x+1'))
>>> g = StateGraph(n)
>>> len(g)
1
>>> for state in g :
... print('%s states known' % len(g))
2 states known
3 states known
4 states known
5 states known
6 states known
6 states known
Call API
return int
: the number of states generated at call time
Method StateGraph.__iter__
def __iter__ (self) :
Iterate over the reachable states (numbers). If needed, the successors of each state are computed just before it is yield. So, if the graph is not complete, getting the predecessors may be wrong during the iteration.
Warning: the net may have an infinite state graph, which is not checked. So you may enter an infinite iteration.
>>> n = PetriNet('N')
>>> n.add_place(Place('p', [0]))
>>> n.add_transition(Transition('t', Expression('x<5')))
>>> n.add_input('p', 't', Variable('x'))
>>> n.add_output('p', 't', Expression('x+1'))
>>> g = StateGraph(n)
>>> for state in g :
... print('state %s is %r' % (state, g.net.get_marking()))
state 0 is Marking({'p': MultiSet([0])})
state 1 is Marking({'p': MultiSet([1])})
state 2 is Marking({'p': MultiSet([2])})
state 3 is Marking({'p': MultiSet([3])})
state 4 is Marking({'p': MultiSet([4])})
state 5 is Marking({'p': MultiSet([5])})
>>> n = PetriNet('N')
>>> n.add_place(Place('p', [0]))
>>> n.add_transition(Transition('t'))
>>> n.add_input('p', 't', Variable('x'))
>>> n.add_output('p', 't', Expression('(x+1) % 5'))
>>> g = StateGraph(n)
>>> for state in g :
... print('state %s is %r' % (state, g.net.get_marking()))
state 0 is Marking({'p': MultiSet([0])})
state 1 is Marking({'p': MultiSet([1])})
state 2 is Marking({'p': MultiSet([2])})
state 3 is Marking({'p': MultiSet([3])})
state 4 is Marking({'p': MultiSet([4])})
Method StateGraph.build
def build (self) :
Method StateGraph.completed
def completed (self) :
Check if all the reachable markings have been explored.
>>> n = PetriNet('N')
>>> n.add_place(Place('p', [0]))
>>> n.add_transition(Transition('t', Expression('x<5')))
>>> n.add_input('p', 't', Variable('x'))
>>> n.add_output('p', 't', Expression('x+1'))
>>> g = StateGraph(n)
>>> for state in g :
... print('%s %s' % (state, g.completed()))
0 False
1 False
2 False
3 False
4 False
5 True
Call API
return bool
:True
if the graph has been completely computed,False
otherwise
Method StateGraph.todo
def todo (self) :
Return the number of states whose successors are not yet computed.
>>> n = PetriNet('N')
>>> n.add_place(Place('p', [0]))
>>> n.add_transition(Transition('t', Expression('x<5')))
>>> n.add_input('p', 't', Variable('x'))
>>> n.add_output('p', 't', Expression('x+1'))
>>> g = StateGraph(n)
>>> for state in g :
... print('%s %s' % (state, g.todo()))
0 1
1 1
2 1
3 1
4 1
5 0
Call API
return int
: the number of pending states