Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Petri net refactor #21

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open

Petri net refactor #21

wants to merge 10 commits into from

Conversation

AdamBanham
Copy link
Owner

Code related to Petri nets in the repo has been broken down into separated files in a new module petrinets. The refactor introduces the following existing structures across the following inner modules.

Net types:

  • PetriNets (pn):
    • Places, Transitions (without weight now), Arcs
      • To keep backwards compatibility, you can pass ids as ints, or strings. But UserWarnings will appear if you use int ids when exporting.
      • To keep previous behaviour, when hashing or equality check occurs we use the str of the id, i.e. str(n.id) is always used.
    • LabelledPetriNet
      • Now precomputes presets and postsets. Tthe methods preset(node) and postset(node) return the set of preset/postset nodes based on the given inpuit.
      • Markings have been removed from structure.
    • PetriNetMarking
      • A simple structure to store what places are marked
      • Introduced some shortcuts to modify
      • place in marking, is place marked in the marking
      • marking[place], returns the marking of that place or zero
      • for place in marking, iterates over the marked places in the marking
      • mark + omark, joins the markings over their marked places
      • mark - omark, reduces all marked places in mark by their counterpart in omark, no negatives allowed
      • mark << omark, check whether mark is a subset of omark.
    • AcceptingPetriNet
      • A simple structure to store information about initial and final markings
    • PetriNetSemantics
      • A structure to enforce the execution semantics of the net, takes the AcceptingPretiNet and a marking.
    • ExecutablePetriNet
      • A simple structure to store all information to execute runs over a given PetriNet.
    • get_execution_semantics
      • A simple shortcut function to jump from AcceptingPetriNet to executable version.
  • Data Petri Nets (dpn)
    • GuardedTransition
      • Adds a guard to the transition
    • and thin wrappers that extend pn counterparts and update hint types:
      • PetriNetWithData, AcceptingDataPetriNet, DataPetriNetSemantics, ExecutableDataPetriNet, get_execution_semantics
  • Weighted Petri Nets (wpn)
    • WeightedTransition
      • adds a weight to transition
    • The BuildablePetriNet has been moved into this file and updated to keep original behaviour of making transitions with weights.
    • and thin wrappers that extend pn counterparts and update hint types:
      • WeightedPetriNet, WeightedAcceptingPetriNet, WeightedPetriNetSemantics, ExcutableWeightedPetriNet, get_execution_semantics

Import and Exporting pnml files:

  • reading read:
    • parse_pnml_into_lpn, reads in a LabelledPetriNet or AcceptingPetriNet if marking information is present.
    • parse_pnml_into_wpn, read in a WeightedPetriNet or WeightedAcceptingPetriNet -- same as above
    • parse_pnml_for_dpn, read in a PetriNetWithData or AcceptingDataPetriNet -- same as above
  • exporting export:
    • export_net_to_pnml, exports the given net with as much information as possible, works for all types.

I have moved guard implementation for dpns to be in the same dir and moved all other existing methods in the previous petrinet.py file to into either utils.py or dot.py. There is another file I created for prettier dot files for lpns in the upper directory, but unsure whos should be the goto.

Leaving the possible refactor for reading and importing using a chain of responsibility for another day.

@adamburkegh you may wish to suggest stochastic over weighted, let me know.

While refactoring, I ensured that all previous behaviour expected by the current set of tests is passing. Updated the tests to reflect the file changes and refactoring for types of nets.

Copy link
Collaborator

@adamburkegh adamburkegh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it's mostly code moves, I think it will be much less painful if we fix the small things I've highlighted and then commit. I really don't want to end up with a snowball patch from hell.

pmkoalas/models/petrinets/dot.py Outdated Show resolved Hide resolved
pmkoalas/models/petrinets/export.py Show resolved Hide resolved
pmkoalas/models/petrinets/read.py Show resolved Hide resolved
def weight(self,value:float) -> None:
self._weight = value

# data model methods
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are they?

Copy link
Owner Author

@AdamBanham AdamBanham Feb 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically data model is a bit more of a general term, but I use this term to refer to emulating behaviour https://docs.python.org/3/reference/datamodel.html#emulating-generic-types. But the data model is very broad.

pmkoalas/models/petrinets/wpn.py Outdated Show resolved Hide resolved
@@ -108,7 +109,7 @@ class ParseException(Exception):
def create_net(net_title="", net_text="") -> LabelledPetriNet:
return PetriNetFragmentParser().create_net(net_title,net_text)

def parse_net_fragments(net_title:str, *fragments:str) -> LabelledPetriNet:
def parse_net_fragments(net_title:str, *fragments:str) -> WeightedPetriNet:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it may not make sense in the same patch, but the better behaviour here would be to return a different type based on the text passed in. So if you don't included weighted transitions, you don't get a weighted net.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, what if I mix both? Keeping it simple may be easier for the future. Outside of type instance checks the wpn is functionally equivalent, so the user does not lose anything in returning a wpn.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you are right that WPN is a direct subclass of PN so it is ok at the moment. It would also be good to keep it open to extension (eg a DPN) without breaking the API contract.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In terms of mixing both, I guess you mean weighted and unweighted in the same net, that would not be a valid WPN though.

tests/models/test_petrinet.py Outdated Show resolved Hide resolved
if isinstance(tran.tid, int):
wstring = f"Transition {tran} has an integer id, this is not " \
+"recommended, importing into others may break."
warn(wstring)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having something allowed but spitting warnings all over I/O is irritating. It would be better not to warn, but parameterize whether to override with ProM-compatible ids.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would you be okay with being a debug-level message or gone all together?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to kill the I/O side effect and the complicating runtime conditional. But it would be good to mention this integer / uuid stuff as part of a package doc comment that describes the design context. Eg how some of the important importers work and how that constrains the way people may want to use the export function. But also the advantages of simple integer ids during development and other uses.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair enough, maybe we should consider that as a general tennet? Removed warns and adding some design considerations to pn.py.

pmkoalas/models/petrinets/export.py Outdated Show resolved Hide resolved
@AdamBanham
Copy link
Owner Author

@adamburkegh LGTY enough for a merge onto main and we deal with anything else directly?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants