reachability.alc 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. include "primitives.alh"
  2. include "modelling.alh"
  3. include "object_operations.alh"
  4. Boolean function reachability_graph(model : Element):
  5. Element workset
  6. Element transition_vectors_produce
  7. Element transition_vectors_consume
  8. Element all_transitions
  9. Element all_transitions_original
  10. Element tv
  11. Element links
  12. Element mappings
  13. Element reachable_states
  14. Element keys
  15. String transition
  16. String new_transition
  17. String state
  18. String name
  19. String link
  20. String place
  21. String key
  22. Integer link_weight
  23. Integer initial
  24. Integer state_id
  25. Integer next_id
  26. Boolean possible
  27. Element all_places
  28. Element dict_repr
  29. Element new_dict_repr
  30. Element work_unit
  31. Element cache
  32. cache = dict_create()
  33. // Create a dictionary representation for each transition
  34. transition_vectors_produce = dict_create()
  35. transition_vectors_consume = dict_create()
  36. all_transitions = allInstances(model, "PetriNet/Transition")
  37. while (set_len(all_transitions) > 0):
  38. transition = set_pop(all_transitions)
  39. tv = dict_create()
  40. links = allIncomingAssociationInstances(model, transition, "PetriNet/P2T")
  41. while (set_len(links) > 0):
  42. link = set_pop(links)
  43. name = reverseKeyLookup(model["model"], read_edge_src(model["model"][link]))
  44. link_weight = read_attribute(model, link, "weight")
  45. dict_add_fast(tv, name, link_weight)
  46. dict_add_fast(transition_vectors_consume, transition, tv)
  47. tv = dict_create()
  48. links = allOutgoingAssociationInstances(model, transition, "PetriNet/T2P")
  49. while (set_len(links) > 0):
  50. link = set_pop(links)
  51. name = reverseKeyLookup(model["model"], read_edge_dst(model["model"][link]))
  52. link_weight = read_attribute(model, link, "weight")
  53. dict_add_fast(tv, name, link_weight)
  54. dict_add_fast(transition_vectors_produce, transition, tv)
  55. workset = set_create()
  56. all_places = allInstances(model, "PetriNet/Place")
  57. dict_repr = dict_create()
  58. while (set_len(all_places) > 0):
  59. place = set_pop(all_places)
  60. dict_add_fast(dict_repr, place, read_attribute(model, place, "tokens"))
  61. all_transitions_original = allInstances(model, "PetriNet/Transition")
  62. mappings = dict_create()
  63. state_id = 0
  64. next_id = 1
  65. reachable_states = dict_create()
  66. dict_add_fast(reachable_states, state_id, dict_repr)
  67. dict_add_fast(mappings, state_id, dict_create())
  68. set_add(workset, state_id)
  69. // And add in the model itself
  70. state = instantiate_node(model, "ReachabilityGraph/InitialState", cast_string(state_id))
  71. instantiate_attribute(model, state, "name", cast_string(state_id))
  72. instantiate_attribute(model, state, "error", False)
  73. keys = dict_keys(dict_repr)
  74. while (set_len(keys) > 0):
  75. key = set_pop(keys)
  76. place = instantiate_node(model, "ReachabilityGraph/Place", "")
  77. instantiate_attribute(model, place, "name", read_attribute(model, key, "name"))
  78. instantiate_attribute(model, place, "tokens", dict_repr[key])
  79. instantiate_link(model, "ReachabilityGraph/Contains", "", state, place)
  80. while (set_len(workset) > 0):
  81. state_id = set_pop(workset)
  82. dict_repr = reachable_states[state_id]
  83. // Compute how the PN behaves with this specific state
  84. // For this, we fetch all transitions and check if they are enabled
  85. all_transitions = set_copy(all_transitions_original)
  86. while (set_len(all_transitions) > 0):
  87. transition = set_pop(all_transitions)
  88. keys = dict_keys(transition_vectors_consume[transition])
  89. possible = True
  90. while (set_len(keys) > 0):
  91. key = set_pop(keys)
  92. // Compare the values in the state with those consumed by the transition
  93. if (integer_lt(dict_repr[key], transition_vectors_consume[transition][key])):
  94. // Impossible transition, so discard this one
  95. possible = False
  96. // Alternative implementation of break, which seems to crash JIT sometimes
  97. //break!
  98. keys = set_create()
  99. if (possible):
  100. new_dict_repr = dict_copy(dict_repr)
  101. // Transition can execute, so compute and add the new state based on the consume/produce vectors
  102. keys = dict_keys(transition_vectors_consume[transition])
  103. while (set_len(keys) > 0):
  104. key = set_pop(keys)
  105. dict_overwrite(new_dict_repr, key, integer_subtraction(new_dict_repr[key], transition_vectors_consume[transition][key]))
  106. keys = dict_keys(transition_vectors_produce[transition])
  107. while (set_len(keys) > 0):
  108. key = set_pop(keys)
  109. dict_overwrite(new_dict_repr, key, integer_addition(new_dict_repr[key], transition_vectors_produce[transition][key]))
  110. // Check if this state already has an associated ID
  111. Integer other_state_id
  112. Integer target_id
  113. keys = dict_keys(reachable_states)
  114. target_id = -1
  115. Float start
  116. while (set_len(keys) > 0):
  117. other_state_id = set_pop(keys)
  118. if (dict_eq(reachable_states[other_state_id], new_dict_repr)):
  119. target_id = other_state_id
  120. // Alternative implementation of break, which seems to crash JIT sometimes
  121. //break!
  122. keys = set_create()
  123. if (target_id == -1):
  124. // New state
  125. target_id = next_id
  126. next_id = next_id + 1
  127. // Add to all data structures
  128. dict_add_fast(reachable_states, target_id, new_dict_repr)
  129. dict_add_fast(mappings, target_id, dict_create())
  130. set_add(workset, target_id)
  131. // And add in the model itself
  132. state = instantiate_node(model, "ReachabilityGraph/State", cast_string(target_id))
  133. instantiate_attribute(model, state, "name", cast_string(target_id))
  134. instantiate_attribute(model, state, "error", False)
  135. keys = dict_keys(new_dict_repr)
  136. Element sub
  137. String name
  138. while (set_len(keys) > 0):
  139. key = set_pop(keys)
  140. name = read_attribute(model, key, "name")
  141. if (bool_not(dict_in(cache, name))):
  142. dict_add_fast(cache, name, dict_create())
  143. sub = cache[name]
  144. if (bool_not(dict_in(sub, new_dict_repr[key]))):
  145. place = instantiate_node(model, "ReachabilityGraph/Place", "")
  146. instantiate_attribute(model, place, "name", name)
  147. instantiate_attribute(model, place, "tokens", new_dict_repr[key])
  148. dict_add_fast(sub, new_dict_repr[key], place)
  149. else:
  150. place = sub[new_dict_repr[key]]
  151. instantiate_link(model, "ReachabilityGraph/Contains", "", state, place)
  152. // Anyway, we have found a transition, which we should store
  153. dict_add_fast(mappings[state_id], transition, target_id)
  154. // And also store it in the model itself
  155. new_transition = instantiate_link(model, "ReachabilityGraph/Transition", "", cast_string(state_id), cast_string(target_id))
  156. instantiate_attribute(model, new_transition, "name", read_attribute(model, transition, "name"))
  157. log("# reachable states: " + cast_value(next_id))
  158. return True!