{"id":16411128,"url":"https://github.com/phoe/petri","last_synced_at":"2026-01-30T05:03:06.976Z","repository":{"id":111724281,"uuid":"162049123","full_name":"phoe/petri","owner":"phoe","description":"Implementation of Petri nets in Common Lisp","archived":false,"fork":false,"pushed_at":"2020-04-13T12:12:06.000Z","size":246,"stargazers_count":25,"open_issues_count":1,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-01-06T03:23:56.224Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/phoe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-12-16T23:04:16.000Z","updated_at":"2022-08-17T03:45:45.000Z","dependencies_parsed_at":null,"dependency_job_id":"30ff0dcc-0c6f-4be8-b56a-3c1c75eabef5","html_url":"https://github.com/phoe/petri","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phoe%2Fpetri","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phoe%2Fpetri/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phoe%2Fpetri/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phoe%2Fpetri/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/phoe","download_url":"https://codeload.github.com/phoe/petri/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240416290,"owners_count":19797782,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-11T06:44:36.028Z","updated_at":"2026-01-30T05:03:06.925Z","avatar_url":"https://github.com/phoe.png","language":"Common Lisp","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PETRI\n\nA Common Lisp implementation of\n[Petri nets](https://en.wikipedia.org/wiki/Petri_net).\n\n## Warning\n\nThis README is beta-quality. It will be fixed and extended one day.\n\n## Introduction\n\nThe original Petri net notation mentions transitions and places. Because the\nterm \"place\" already has a defined meaning in Common Lisp, I have decided to\ninstead use the term \"bag\" in this library. The implementations of bags I use is\n[`PHOE-TOOLBOX/BAG`](https://github.com/phoe/phoe-toolbox/) and all bags may be\nqueried using the interface defined in that system: `BAG-CONTENTS`,\n`BAG-INSERT`, `BAG-REMOVE`, and `BAG-COUNT`.\n\nEach Petri net is a funcallable object and invoking a Petri net happens via\nfuncalling it.\n\nThe keyword arguments to a Petri net are:\n* `:COMPRESS` - states whether the bags should be compressed after each Petri\nnet call;\n* `:IGNORE-ERRORS` - states whether all errors should be silently ignored during\nPetri net execution. This option is useful if error handling happens inside\ntransition functions via programmer code.\n\nA Petri net returns two values upon being funcalled: the primary return value is\nitself, and the secondary value is a boolean stating whether any errors have\noccurred during the execution of the Petri net. (This value is only useful if\n`:IGNORE-ERRORS` has been set.)\n\nPetri nets are immutable after their creation. Their only mutable parts are the\nbag objects referenced by them.\n\nEach transition in the Petri net is specified by a list of input bag specs, a\nlist of output bag specs, and a callback function. Each bag spec A single bag\nspec may be one of the following:\n\n* a symbol, denoting a bag with the default count 1,\n* a list in form of `(symbol count)`, denoting a bag with name `symbol` and\n  count `count`.\n\nIn case of input bags, the count denotes how many tokens must be present in a\nbag for the transition to fire. When a transition fires, these tokens are\nautomatically removed from the bag and passed to the callback by means of an\ninput hash table, where the keys are bag names and values are lists of tokens .\n\nIn case of output bags, the count denotes how many tokens must be pushed by the\ncallback into the output hash table under the key being the bag's hash table. If\nthe number of tokens pushed by the transition does not match the spec, an\nexecution-time error is signaled.\n\nA special kind of an input bag spec is a list in form of `(symbol !)`, where `!`\nis any symbol with name `\"!\"`. This spec denotes an inhibitor edge, meaning that\nthe bag with the name `symbol` must be empty in order for the transition to\nfire.\n\nA special kind of an output bag spec is a list in form of `(symbol *)`, where\n`*` is any symbol with name `\"*\"`. This spec denotes a wildcard edge, meaning\nthat the transition may push any amount of tokens to the bag with the name\n`symbol`.\n\nEach callback is a function that must accept a pair of arguments: an input hash\ntable and an output hash table. The input hash table provides input for the\ncallback and the output hash table is used for sending output. The return value\nof the callback is ignored.\n\nThe input hash table is fresh may freely be mutated by the callback.\n\n`PETRI` pre-creates the keys in output hash table, so the callback is allowed to\naccess the keys of that hash table in order to determine how to behave. In\nparticular, the callback is allowed to do nothing in order to be considered\nvalid.\n\n`PETRI` solves the nondeterminism of Petri nets by means of random selection.\nEach time the code searches for an available transition, all transitions are\nsearched in random order. Each time a token is removed from a bag, it is removed\nat random.\n\nExecution of a Petri net stops when no more transitions are available to fire.\nIn particular, this means that calling a Petri net may loop indefinitely.\n\n## ASDF systems\n\n### PETRI\n\nThis ASDF system defines the base code implementing Petri nets.\n\nA Petri net (an instance of class `PETRI-NET`) may be created in two ways: using\nfunctional syntax (via the `MAKE-PETRI-NET` function) and using declarative\nsyntax (via the `PETRI-NET` macro.) An individual bag of a Petri net may be\naccessed using the `BAGS-OF` function, which accepts a Petri net and the name of\nthe bag in question. A list of all bag names may be accessed via `BAG-NAMES`.\n\nCondition types defined by this system are `PETRI-NET-ERROR`, which is the\nsupertype of all errors related to Petri nets, and `SIMPLE-PETRI-NET-ERROR`,\nwhich is a `PETRI-NET-ERROR` that is also a `SIMPLE-ERROR`. The convenience\nfunction `PETRI-NET-ERROR` exists for signaling this error.\n\n### PETRI/THREADED\n\nThis ASDF system contains code for a multithreaded implementation of Petri net\nthat uses raw `BORDEAUX-THREADS` for execution. Each transition is executed in a\nseparate thread as soon as enough tokens are available for it to fire.\n\nInstances of threaded Petri nets (instances of class `THREADED-PETRI-NET`) may\nbe created using the `THREADED-PETRI-NET` macro or the `MAKE-THREADED-PETRI-NET`\nfunction.\n\nErrors cause the transition to return and are propagated to the thread calling\nthe Petri net along with the backtrace, wrapped in a condition of type\n`THREADED-PETRI-NET-ERROR`.\n\n### PETRI/GRAPH\n\nThis ASDF system contains code for integrating `PETRI` with `CL-DOT`.\n\nLoading this system defines methods on the generic functions in `CL-DOT`'s\n`GRAPH-OBJECT` protocol that allow Petri nets to be graphed.\n\nThe only symbol exported by this package is `DISPLAY-GRAPH`, a convenience\nfunction which accepts a Petri net, generates a PNG graph in a temporary\ndirectory and automatically executes it using `xdg-open`.\n\n### PETRI/TEST\n\nThis ASDF system contains tests for `PETRI` and `PETRI-THREADED`.\n\nYou do not need to explicitly load this system to run the tests; instead, use\n`(asdf:test-system :petri)`.\n\nSee the file [test.lisp](test.lisp) for working examples in form of unit\ntests. Each working test is run in four manners: using functional and\ndeclarative syntax, and using single-threaded and multithreaded implementations.\n\n## Examples\n\n### Negation\n\n```common-lisp\n;; Define the callback function for the transition.\n(flet ((callback (input output)\n         (push (- (pop (gethash 'foo input))) (gethash 'bar output))))\n  ;; Create the petri net object.\n  (let ((petri-net (make-petri-net\n                    ;; Define bags FOO and BAR.\n                    '(foo bar)\n                    ;; Define a single transition which takes one token from FOO\n                    ;; and outputs one token to BAR.\n                    `((((foo 1)) ((bar 1)) ,#'callback)))))\n    ;; Populate bag FOO with data.\n    (dolist (i '(1 2 3))\n      (bag-insert (bag-of petri-net 'foo) i))\n    ;; Funcall the Petri net.\n    (funcall petri-net)\n    ;; Access the contents of bag BAR.\n    (bag-contents (bag-of petri-net 'bar))))\n;; =\u003e #(-1 -3 -2)\n```\n\n![Generated negation graph](example-negation.png)\n\nDue to the non-determinism of the Petri net, the output vector may have its\nelements in any order.\n\n### Maybe-negation\n\n```common-lisp\n;; Define the callback functions for the transition: one which negates its\n;; arguments and the other which passes them without any change.\n(flet ((callback-negation (input output)\n         (push (- (pop (gethash 'foo input))) (gethash 'bar output)))\n       (callback-identity (input output)\n         (push (pop (gethash 'foo input)) (gethash 'bar output))))\n  ;; Create the petri net object.\n  (let ((petri-net (make-petri-net\n                    ;; Define bags FOO and BAR.\n                    '(foo bar)\n                    ;; Define two transitions:\n                    ;; * one that takes one token from FOO, outputs one token to\n                    ;;   BAR, and calls CALLBACK-IDENTITY,\n                    ;; * one that takes one token from FOO, outputs one token to\n                    ;;   BAR, and calls CALLBACK-NEGATION.\n                    `((((foo 1)) ((bar 1)) ,#'callback-negation)\n                      (((foo 1)) ((bar 1)) ,#'callback-identity)))))\n    ;; Populate bag FOO with data.\n    (dolist (i '(1 2 3 4 5 6 7 8 9 0))\n      (bag-insert (bag-of petri-net 'foo) i))\n    ;; Funcall the Petri net.\n    (funcall petri-net)\n    ;; Access the contents of bag BAR.\n    (bag-contents (bag-of petri-net 'bar))))\n;; =\u003e #(-1 -9 -3 8 -4 5 0 6 -2 -7)\n;; =\u003e #(-5 8 0 2 -4 -9 -3 -1 -7 -6)\n;; =\u003e #(-4 -8 -1 -2 -5 -7 -6 3 0 9)\n;; =\u003e ...\n```\n\n![Generated maybe-negation graph](example-maybe-negation.png)\n\nDue to the non-determinism of the Petri net, the output vector may have its\nelements in any order, and any element might have been negated.\n\n### Graphs\n\nA more complex Petri net may be visualized using the `PETRI/GRAPH` system.\n\n```common-lisp\n(threaded-petri-net ()\n  (update-digo-data-p -\u003e #'dl-digo-data -\u003e new-digo-data-p)\n  (new-digo-data-p old-digo-data-p -\u003e #'save-digo-data)\n  (credentials -\u003e #'login -\u003e cookie-jars\n               -\u003e #'dl-account -\u003e accounts (accounts-furres *))\n  (accounts-furres (old-digo-data-p !)\n                   -\u003e #'dl-furre\n                   -\u003e furres\n                     (furres-costumes *)\n                     (furres-portraits *)\n                     (furres-specitags *)\n                     furres-images)\n  (furres-costumes -\u003e #'dl-costume -\u003e costumes)\n  (furres-portraits -\u003e #'dl-portrait -\u003e portraits)\n  (furres-specitags -\u003e #'dl-specitag -\u003e specitags)\n  (furres-images -\u003e #'dl-image-list -\u003e (image-metadata *))\n  (image-metadata -\u003e #'dl-image -\u003e images))\n```\n\n![Generated complex graph](example-complex.png)\n\nIn the above example, the programmer's intent was to store input in the\n`CREDENTIALS` bag read and output from bags `ACCOUNTS`, `IMAGES`, `FURRES`,\n`COSTUMES`, `PORTRAITS`, and `SPECITAGS` when the Petri net finishes executing.\nEach transition accepts one token from the input bag and store either one token\ninto each output bag (edges without labels) or an arbitrary number of tokens\ninto each output bag (edges labeled with `*`).\n\nAdditionally, an optional constraint is placed, in which `DL-FURRE` may not\nexecute before `DL-DIGO-DATA` finishes executing. This is achieved via an extra\nplace, `OLD-DIGO-DATA-P`, and an inhibitor edge leading from it. If this\nconstraint is meant to be activated, then a pair of arbitrary tokens should be\nplaced on `UPDATE-DIGO-DATA-P` and `OLD-DIGO-DATA-P`.\n\n## Extending PETRI\n\nThis system is currently not designed to be extensible. See the class\ndefinitions and generic functions defined in the file `petri.lisp` for details\non the internal working of the system.\n\n## License\n\nCopyright © 2018 Michał \"phoe\" Herda.\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the “Software”), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphoe%2Fpetri","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphoe%2Fpetri","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphoe%2Fpetri/lists"}