lib/y_petri/transition.rb in y_petri-2.1.3 vs lib/y_petri/transition.rb in y_petri-2.1.6
- old
+ new
@@ -1,13 +1,12 @@
# encoding: utf-8
require_relative 'transition/arcs'
require_relative 'transition/cocking'
-require_relative 'transition/init'
-require_relative 'transition/timed'
-require_relative 'transition/ordinary_timeless'
-require_relative 'transition/assignment'
+require_relative 'transition/construction_convenience'
+require_relative 'transition/types'
+require_relative 'transition/usable_without_world'
# Transitions -- little boxes in Petri net drawings -- represent atomic
# operations on the Petri net's marking.
#
# === Domain and codomin
@@ -90,64 +89,109 @@
# remains a possibility of creating vanilla (_functionless_) transitions by not
# specifying any rate / action, while specifying the stoichiometry. Action
# closure as per C. A. Petri is automatically constructed for these.
#
class YPetri::Transition
- include NameMagic
- include YPetri::World::Dependency
+ ★ NameMagic # ★ means include
+ ★ YPetri::World::Dependency
+ ★ UsableWithoutWorld
+ ★ Arcs
+ ★ Cocking
+ ★ ConstructionConvenience
+ ★ Types
class << self
- include YPetri::World::Dependency
+ ★ YPetri::World::Dependency
end
- delegate :world, to: "self.class"
-
- BASIC_TRANSITION_TYPES = {
+ TYPES = {
+ T: "timed",
+ t: "timeless",
+ S: "stoichiometric",
+ s: "non-stoichiometric",
+ A: "assignment",
+ a: "non-assignment",
TS: "timed stoichiometric",
tS: "timeless stoichiometric",
Ts: "timed nonstoichiometric",
ts: "timeless nonstoichiometric"
}
- def TS?; type == :TS end
- def Ts?; type == :Ts end
- def tS?; type == :tS end
- def ts?; type == :ts end
+ delegate :world, to: "self.class"
+ # Transition class represents many different kinds of Petri net transitions.
+ # It makes the constructor syntax a bit more polymorphic. The type of the
+ # transition to construct is mostly inferred from the constructor arguments.
+ #
+ # Mandatorily, the constructor will always need a way to determine the domain
+ # (upstream arcs) and codomain (downstream arcs) of the transition. Also, the
+ # constructor must have a way to determine the transition's action. This is
+ # best explained by examples -- let us have 3 places A, B, C, for whe we will
+ # create different kinds of transitions:
+ #
+ #
+ # ==== TS (timed stoichiometric)
+ #
+ # Rate closure and stoichiometry has to be supplied. Rate closure arity should
+ # correspond to the domain size. Return arity should be 1 (to be multiplied by
+ # the stoichiometry vector, as in all other stoichiometric transitions).
+ #
+ # Transition.new stoichiometry: { A: -1, B: 1 },
+ # rate: -> a { a * 0.5 }
+ #
+ #
+ # ==== Ts (timed nonstoichiometric)
+ #
+ # Rate closure has to be supplied, whose arity should match the domain, and
+ # output arity codomain.
+ #
+ # ==== tS (timeless stoichiometric)
+ #
+ # Stoichiometry has to be supplied, action closure is optional. If supplied,
+ # its return arity should be 1 (to be multiplied by the stoichiometry vector).
+ #
+ # ==== ts transitions (timeless nonstoichiometric)
+ #
+ # Action closure is expected with return arity equal to the codomain size:
+ #
+ # Transition.new upstream_arcs: [A, C], downstream_arcs: [A, B],
+ # action_closure: proc { |m, x|
+ # if x > 0 then [-(m / 2), (m / 2)]
+ # else [1, 0] end
+ # }
+ #
+ def initialize *args, &block
+ check_in_arguments *args, &block # the big job
+ extend( if timed? then Type_T
+ elsif assignment_action? then Type_A
+ else Type_t end )
+ inform_upstream_places # that they have been connected
+ inform_downstream_places # that they have been connected
+ uncock # initialize in the uncocked state
+ end
+
# Domain, or 'upstream arcs', is a collection of places, whose marking
# directly affects the transition's action.
#
attr_reader :domain
- alias :domain_arcs :domain
- alias :domain_places :domain
- alias :upstream :domain
- alias :upstream_arcs :domain
- alias :upstream_places :domain
+ alias domain_arcs domain
+ alias domain_places domain
+ alias upstream domain
+ alias upstream_arcs domain
+ alias upstream_places domain
# Codomain, 'downstream arcs', or 'action arcs', is a collection of places,
# whose marking is directly changed by this transition's firing.
#
attr_reader :codomain
- alias :codomain_arcs :codomain
- alias :codomain_places :codomain
- alias :downstream :codomain
- alias :downstream_arcs :codomain
- alias :downstream_places :codomain
- alias :action_arcs :codomain
+ alias codomain_arcs codomain
+ alias codomain_places codomain
+ alias downstream codomain
+ alias downstream_arcs codomain
+ alias downstream_places codomain
+ alias action_arcs codomain
- # Is the transition stoichiometric?
- #
- def stoichiometric?; @stoichiometric end
- alias :S? :stoichiometric?
-
- # Is the transition nonstoichiometric? (Opposite of #stoichiometric?)
- #
- def nonstoichiometric?
- not stoichiometric?
- end
- alias :s? :nonstoichiometric?
-
# Stoichiometry (implies that the transition is stoichiometric).
#
attr_reader :stoichiometry
# Stoichiometry as a hash of pairs:
@@ -168,79 +212,21 @@
# both of which are treated as aliases of _rate_. The decision between
# discrete and continuous computation is a concern of the simulation.
# Rate closure arity should correspond to the transition's domain.
#
attr_reader :rate_closure
- alias :rate :rate_closure
- alias :flux_closure :rate_closure
- alias :flux :rate_closure
- alias :propensity_closure :rate_closure
- alias :propensity :rate_closure
+ alias rate rate_closure
+ alias flux_closure rate_closure
+ alias flux rate_closure
+ alias propensity_closure rate_closure
+ alias propensity rate_closure
# For rateless transition, action closure must be present. Action closure
# input arguments must correspond to the domain places, and for timed
# transitions, the first argument of the action closure must be Δtime.
#
attr_reader :action_closure
- alias :action :action_closure
-
- # Does the transition's action depend on delta time?
- #
- def timed?
- @timed
- end
- alias T? timed?
-
- # Is the transition timeless? (Opposite of #timed?)
- #
- def timeless?
- not timed?
- end
- alias t? timeless?
-
- # Is the transition functional?
- # Explanation: If rate or action closure is supplied, a transition is always
- # considered 'functional'. Otherwise, it is considered not 'functional'.
- # Note that even transitions that are not functional still have standard
- # action acc. to Petri's definition. Also note that a timed transition is
- # necessarily functional.
- #
- def functional?
- @functional
- end
-
- # Opposite of #functional?
- #
- def functionless?
- not functional?
- end
-
- # Reports the transition's membership in one of the 4 basic types:
- #
- # 1. TS .... timed stoichiometric
- # 2. tS .... timeless stoichiometric
- # 3. Ts .... timed nonstoichiometric
- # 4. ts .... timeless nonstoichiometric
- #
- # plus the fifth type
- #
- # 5. A .... assignment transitions
- #
- def type
- return :A if assignment_action?
- timed? ? ( stoichiometric? ? :TS : :Ts ) : ( stoichiometric? ? :tS : :ts )
- end
-
- # Is it an assignment transition? (Transitions with 'assignment action'
- # completely replace their codomain's marking.)
- #
- def assignment_action?; @assignment_action end
- alias :assignment? :assignment_action?
- alias :A? :assignment_action?
-
- # Is it a non-assignment transition? (Opposite of +#A?+)
- #
- def a?; ! assignment_action? end
+ alias action action_closure
# Zero action.
#
def zero_action
codomain.map { 0 }