{ "cells": [ { "cell_type": "markdown", "id": "cce8253b-c717-4393-8033-653ea8e85cdb", "metadata": {}, "source": [ "# Pizza example\n", "\n", "In this example, we'll import the raw ontology constructor, update it with our own additional pizza-themed entries, and see how a few features of the pyiron ontology play out when building workflows." ] }, { "cell_type": "code", "execution_count": 1, "id": "c81ee6cf-5539-4e94-b6f8-7e53a94b448f", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "* Owlready2 * Warning: optimized Cython parser module 'owlready2_optimized' is not available, defaulting to slower Python implementation\n" ] } ], "source": [ "import owlready2 as owl\n", "from pyiron_ontology import Constructor\n" ] }, { "cell_type": "markdown", "id": "3046b0b3-1e1a-4701-bd8c-b84a6c5a741a", "metadata": {}, "source": [ "First, we instantiate a new constructor, then use owlready's `with` syntax to add new entities.\n", "\n", "Very often, we want to describe features of some entity that are _mutually exclusive_. In materials science, we might thing about a material being \"bulk\", i.e. \"defect-free\", or \"defected\", i.e. containing one or more grain boundaries, phase boundaries, surfaces, vacancies, etc. In our pizza example we could think of a pizza being \"vegetarian\" or containing _any nonzero number_ of \"meat\" toppings.\n", "\n", "Handling this mutual exclusion is [a little bit tricky](http://www.cs.man.ac.uk/~rector/swbp/specified_values/specified-values-8-2.html) in the OWL paradigm. In pyiron, we use a subclass based approach where features are represented using subclasses and these subclasses are registered as disjoint -- i.e. no individual can inherit from both of them. We can then have multiple non-exclusionary features by including them all in the `is_a` field when instantiating our classes.\n", "\n", "For exactly this \"vegetarian\" example, you'll also see below that we can leverage owlready's `equivalent_to` field and some good class structure to very succinctly define these mutually-exclusive features." ] }, { "cell_type": "code", "execution_count": 2, "id": "0398a18b-c53c-47b6-9f62-eede354e2861", "metadata": {}, "outputs": [], "source": [ "c = Constructor('pizza')\n", "\n", "with c.onto:\n", " class Flour(c.onto.Generic): pass\n", " class Wheat(Flour): pass\n", " class GlutenFree(Flour): pass\n", " owl.AllDisjoint([GlutenFree, Wheat])\n", "\n", " class Crust(c.onto.Generic): pass\n", " class Thin(Crust): pass\n", " class Regular(Crust): pass\n", " owl.AllDisjoint([Thin, Regular])\n", " class Stuffed(Regular): pass\n", "\n", " class Ingredients(c.onto.Generic): pass\n", " class HasVegetables(Ingredients): pass\n", " class HasMushrooms(HasVegetables): pass\n", " class HasPeppers(HasVegetables): pass\n", " class HasMeat(Ingredients): pass\n", " class HasSalami(HasMeat): pass\n", " class HasBacon(HasMeat): pass\n", " class Vegetarian(Ingredients):\n", " equivalent_to = [Ingredients & owl.Not(HasMeat)]\n", " owl.AllDisjoint([Vegetarian, HasMeat])\n", "\n", " class RawPizza(c.onto.Generic): pass\n", "\n", " class CookedPizza(c.onto.Generic): pass\n", "\n", " owl.AllDisjoint([Flour, Crust, Ingredients, RawPizza, CookedPizza])" ] }, { "cell_type": "markdown", "id": "c5e5b8f4-67ef-4577-9b63-6def669fbcd6", "metadata": {}, "source": [ "Now that we've defined the universe of things our workflows will operate in, we'll define different workflow elements as individuals.\n", "\n", "Here, we'll have a workflow where we buy flour and make a crust, then combine the crust with toppings to make a raw pizza, and finally bake that pizza.\n", "\n", "What type of pizza we wind up with is controlled by the ontological classes for our inputs and outputs, as well as the extra `requirements` that we insist be satisfied in the workflow tree.\n", "\n", "In particular, at the stage of assembling the raw pizza, we indicate that the flour type is a \"transitive requirement\". This is a requirement that is not necessary at this stage, but may (or may not) be necessary farther upstream -- in this case when we're making our crust. Marking it as \"transitive\" ensures that it gets passed along to these upstream workflow steps." ] }, { "cell_type": "code", "execution_count": 3, "id": "e611f6d2-c350-47f4-bcab-902a8f821ea8", "metadata": {}, "outputs": [], "source": [ "buy_wheat_flour = c.onto.Function(\"buy_wheat_flour\")\n", "buy_wheat_flour_out = c.onto.Output(\n", " \"buy_wheat_flour_out\",\n", " output_of=buy_wheat_flour,\n", " generic=Wheat()\n", ")\n", "\n", "buy_corn_flour = c.onto.Function(\"buy_corn_flour\")\n", "buy_corn_flour_out = c.onto.Output(\n", " \"buy_corn_flour_out\",\n", " output_of=buy_corn_flour,\n", " generic=GlutenFree()\n", ")\n", "\n", "make_crust = c.onto.Function(\"make_crust\")\n", "make_crust_inp_flour = c.onto.Input(\n", " name=\"make_crust_inp_flour\",\n", " mandatory_input_of=make_crust,\n", " generic=Flour(),\n", ")\n", "make_crust_out = c.onto.Output(\n", " name=\"make_crust_out\",\n", " output_of=make_crust,\n", " generic=Crust(),\n", ")\n", "\n", "make_thin_crust = c.onto.Function(\"make_thin_crust\")\n", "make_thin_crust_inp_flour = c.onto.Input(\n", " name=\"make_thin_crust_inp_flour\",\n", " mandatory_input_of=make_thin_crust,\n", " generic=Flour(),\n", ")\n", "make_thin_crust_out = c.onto.Output(\n", " name=\"make_thin_crust_out\",\n", " output_of=make_thin_crust,\n", " generic=Thin(),\n", ")\n", "\n", "make_gluten_free_crust = c.onto.Function(\"make_gluten_free_crust\")\n", "make_gluten_free_crust_inp_flour = c.onto.Input(\n", " name=\"make_gluten_free_crust_inp_flour\",\n", " mandatory_input_of=make_gluten_free_crust,\n", " generic=GlutenFree(),\n", ")\n", "make_gluten_free_crust_out = c.onto.Output(\n", " name=\"make_gluten_free_crust_out\",\n", " output_of=make_gluten_free_crust,\n", " generic=Crust(),\n", ")\n", "\n", "add_meat = c.onto.Function(\"add_meat\")\n", "add_meat_inp_ingredients = c.onto.Input(\n", " name=\"add_meat_inp_ingredients\",\n", " mandatory_input_of=add_meat,\n", " generic=HasMeat(),\n", ")\n", "add_meat_inp_crust = c.onto.Input(\n", " name=\"add_meat_inp_crust\",\n", " mandatory_input_of=add_meat,\n", " generic=Crust(),\n", " transitive_requirements=[Flour()]\n", ")\n", "add_meat_out = c.onto.Output(\n", " name=\"add_meat_out\",\n", " output_of=add_meat,\n", " generic=RawPizza()\n", ")\n", "\n", "add_vegetables = c.onto.Function(\"add_vegetables\")\n", "add_vegetables_inp_ingredients = c.onto.Input(\n", " name=\"add_vegetables_inp_ingredients\",\n", " mandatory_input_of=add_vegetables,\n", " generic=HasVegetables(),\n", ")\n", "add_vegetables_inp_crust = c.onto.Input(\n", " name=\"add_vegetables_inp_crust\",\n", " mandatory_input_of=add_vegetables,\n", " generic=Crust(),\n", " transitive_requirements=[Flour()]\n", ")\n", "add_vegetables_out = c.onto.Output(\n", " name=\"add_vegetables_out\",\n", " output_of=add_vegetables,\n", " generic=RawPizza()\n", ")\n", "\n", "canadian = c.onto.Function(\"canadian\")\n", "canadian_inp_ingredients = c.onto.Input(\n", " name=\"canadian_inp_ingredients\",\n", " mandatory_input_of=canadian,\n", " generic=Ingredients(is_a=[HasBacon, HasMushrooms]),\n", ")\n", "canadian_inp_crust = c.onto.Input(\n", " name=\"canadian_inp_crust\",\n", " mandatory_input_of=canadian,\n", " generic=Crust(),\n", " transitive_requirements=[Flour()]\n", ")\n", "canadian_out = c.onto.Output(\n", " name=\"canadian_out\",\n", " output_of=canadian,\n", " generic=RawPizza()\n", ")\n", "\n", "bake_for_omnivor = c.onto.Function(\"bake_for_omnivor\")\n", "bake_for_omnivor_inp = c.onto.Input(\n", " name=\"bake_for_omnivor_inp\",\n", " mandatory_input_of=bake_for_omnivor,\n", " generic=RawPizza(),\n", "\n", ")\n", "bake_for_omnivor_out = c.onto.Output(\n", " name=\"bake_for_omnivor_out\",\n", " output_of=bake_for_omnivor,\n", " generic=CookedPizza()\n", ")\n", "\n", "bake_for_vegetarian = c.onto.Function(\"bake_for_vegetarian\")\n", "bake_for_vegetarian_inp = c.onto.Input(\n", " name=\"bake_for_vegetarian_inp\",\n", " mandatory_input_of=bake_for_vegetarian,\n", " generic=RawPizza(),\n", " requirements=[Vegetarian()]\n", ")\n", "bake_for_vegetarian_out = c.onto.Output(\n", " name=\"bake_for_vegetarian_out\",\n", " output_of=bake_for_vegetarian,\n", " generic=CookedPizza()\n", ")\n", "\n", "bake_stuffed_crust = c.onto.Function(\"bake_stuffed_crust\")\n", "bake_stuffed_crust_inp = c.onto.Input(\n", " name=\"bake_stuffed_crust_inp\",\n", " mandatory_input_of=bake_stuffed_crust,\n", " generic=RawPizza(),\n", " requirements=[Stuffed(), Wheat()]\n", ")\n", "bake_stuffed_crust_out = c.onto.Output(\n", " name=\"bake_stuffed_crust_out\",\n", " output_of=bake_stuffed_crust,\n", " generic=CookedPizza()\n", ")\n", "\n", "bake_dietary_restrictions = c.onto.Function(\"bake_dietary_restrictions\")\n", "bake_dietary_restrictions_inp = c.onto.Input(\n", " name=\"bake_dietary_restrictions_inp\",\n", " mandatory_input_of=bake_dietary_restrictions,\n", " generic=RawPizza(),\n", " requirements=[GlutenFree(), Vegetarian()]\n", ")\n", "bake_dietary_restrictions_out = c.onto.Output(\n", " name=\"bake_dietary_restrictions_out\",\n", " output_of=bake_dietary_restrictions,\n", " generic=CookedPizza()\n", ")" ] }, { "cell_type": "markdown", "id": "fe341bf3-03e8-4292-ab7f-b328a61647ef", "metadata": {}, "source": [ "Finally, we'll re-synchronize our reasoner, to make sure it leverages the ontology to find all the equivalencies, make sure we haven't declared any forbidden individuals (i.e. violating `AllDisjoint`), etc." ] }, { "cell_type": "code", "execution_count": 4, "id": "73b50ceb-7bf3-479b-90b4-9fbf42f87ff8", "metadata": {}, "outputs": [], "source": [ "c.sync()" ] }, { "cell_type": "markdown", "id": "03ef5151-8fb9-4190-a2d3-709dc827782c", "metadata": {}, "source": [ "Alright, let's see the available workflow paths to build some of these pizzas! This is easy with the `get_source_tree` method available on workflow individuals." ] }, { "cell_type": "code", "execution_count": 5, "id": "4f201a3b-b8bd-4b71-bd1d-57bb9eacf48a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "bake_for_omnivor_out\n", "\tbake_for_omnivor\n", "\t\tbake_for_omnivor_inp\n", "\t\t\tadd_meat_out\n", "\t\t\t\tadd_meat\n", "\t\t\t\t\tadd_meat_inp_ingredients\n", "\t\t\t\t\tadd_meat_inp_crust\n", "\t\t\t\t\t\tmake_crust_out\n", "\t\t\t\t\t\t\tmake_crust\n", "\t\t\t\t\t\t\t\tmake_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_corn_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_corn_flour\n", "\t\t\t\t\t\t\t\t\tbuy_wheat_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_wheat_flour\n", "\t\t\t\t\t\tmake_gluten_free_crust_out\n", "\t\t\t\t\t\t\tmake_gluten_free_crust\n", "\t\t\t\t\t\t\t\tmake_gluten_free_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_corn_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_corn_flour\n", "\t\t\t\t\t\tmake_thin_crust_out\n", "\t\t\t\t\t\t\tmake_thin_crust\n", "\t\t\t\t\t\t\t\tmake_thin_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_corn_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_corn_flour\n", "\t\t\t\t\t\t\t\t\tbuy_wheat_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_wheat_flour\n", "\t\t\tcanadian_out\n", "\t\t\t\tcanadian\n", "\t\t\t\t\tcanadian_inp_ingredients\n", "\t\t\t\t\tcanadian_inp_crust\n", "\t\t\t\t\t\tmake_crust_out\n", "\t\t\t\t\t\t\tmake_crust\n", "\t\t\t\t\t\t\t\tmake_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_corn_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_corn_flour\n", "\t\t\t\t\t\t\t\t\tbuy_wheat_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_wheat_flour\n", "\t\t\t\t\t\tmake_gluten_free_crust_out\n", "\t\t\t\t\t\t\tmake_gluten_free_crust\n", "\t\t\t\t\t\t\t\tmake_gluten_free_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_corn_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_corn_flour\n", "\t\t\t\t\t\tmake_thin_crust_out\n", "\t\t\t\t\t\t\tmake_thin_crust\n", "\t\t\t\t\t\t\t\tmake_thin_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_corn_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_corn_flour\n", "\t\t\t\t\t\t\t\t\tbuy_wheat_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_wheat_flour\n", "\t\t\tadd_vegetables_out\n", "\t\t\t\tadd_vegetables\n", "\t\t\t\t\tadd_vegetables_inp_ingredients\n", "\t\t\t\t\tadd_vegetables_inp_crust\n", "\t\t\t\t\t\tmake_crust_out\n", "\t\t\t\t\t\t\tmake_crust\n", "\t\t\t\t\t\t\t\tmake_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_corn_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_corn_flour\n", "\t\t\t\t\t\t\t\t\tbuy_wheat_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_wheat_flour\n", "\t\t\t\t\t\tmake_gluten_free_crust_out\n", "\t\t\t\t\t\t\tmake_gluten_free_crust\n", "\t\t\t\t\t\t\t\tmake_gluten_free_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_corn_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_corn_flour\n", "\t\t\t\t\t\tmake_thin_crust_out\n", "\t\t\t\t\t\t\tmake_thin_crust\n", "\t\t\t\t\t\t\t\tmake_thin_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_corn_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_corn_flour\n", "\t\t\t\t\t\t\t\t\tbuy_wheat_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_wheat_flour\n" ] } ], "source": [ "bake_for_omnivor_out.get_source_tree().render()" ] }, { "cell_type": "markdown", "id": "1a6094f7-4e7b-4d27-ab12-1e9f6b93d33a", "metadata": {}, "source": [ "We didn't place _any_ restrictions on that pizza, so we see _all_ the possible pizza-making workflows available in our ontology.\n", "\n", "Note how the `canadian` ingredients demonstrate using `is_a` to declare a class with multiple _non-exclusive_ properties." ] }, { "cell_type": "code", "execution_count": 6, "id": "57320f85-e56b-4ce4-aad3-6cc0dbdbf002", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "bake_for_vegetarian_out\n", "\tbake_for_vegetarian\n", "\t\tbake_for_vegetarian_inp\n", "\t\t\tadd_vegetables_out\n", "\t\t\t\tadd_vegetables\n", "\t\t\t\t\tadd_vegetables_inp_ingredients\n", "\t\t\t\t\tadd_vegetables_inp_crust\n", "\t\t\t\t\t\tmake_crust_out\n", "\t\t\t\t\t\t\tmake_crust\n", "\t\t\t\t\t\t\t\tmake_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_corn_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_corn_flour\n", "\t\t\t\t\t\t\t\t\tbuy_wheat_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_wheat_flour\n", "\t\t\t\t\t\tmake_gluten_free_crust_out\n", "\t\t\t\t\t\t\tmake_gluten_free_crust\n", "\t\t\t\t\t\t\t\tmake_gluten_free_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_corn_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_corn_flour\n", "\t\t\t\t\t\tmake_thin_crust_out\n", "\t\t\t\t\t\t\tmake_thin_crust\n", "\t\t\t\t\t\t\t\tmake_thin_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_corn_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_corn_flour\n", "\t\t\t\t\t\t\t\t\tbuy_wheat_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_wheat_flour\n" ] } ], "source": [ "bake_for_vegetarian_out.get_source_tree().render()" ] }, { "cell_type": "markdown", "id": "94b876ac-6988-4bee-8ae2-2102496cf70c", "metadata": {}, "source": [ "The ontology can tell that `add_vegetables` gives an output that is consistent with the `equivalent_to` definition of `Vegetarian()`! The `add_meat` and `canadian` functions both return ingredients with meat in them, so these are excluded from our workflow tree." ] }, { "cell_type": "code", "execution_count": 7, "id": "c6d62fc7-7b4f-4f3c-a0b0-f2227087f368", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "bake_stuffed_crust_out\n", "\tbake_stuffed_crust\n", "\t\tbake_stuffed_crust_inp\n", "\t\t\tadd_meat_out\n", "\t\t\t\tadd_meat\n", "\t\t\t\t\tadd_meat_inp_ingredients\n", "\t\t\t\t\tadd_meat_inp_crust\n", "\t\t\t\t\t\tmake_crust_out\n", "\t\t\t\t\t\t\tmake_crust\n", "\t\t\t\t\t\t\t\tmake_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_wheat_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_wheat_flour\n", "\t\t\tcanadian_out\n", "\t\t\t\tcanadian\n", "\t\t\t\t\tcanadian_inp_ingredients\n", "\t\t\t\t\tcanadian_inp_crust\n", "\t\t\t\t\t\tmake_crust_out\n", "\t\t\t\t\t\t\tmake_crust\n", "\t\t\t\t\t\t\t\tmake_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_wheat_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_wheat_flour\n", "\t\t\tadd_vegetables_out\n", "\t\t\t\tadd_vegetables\n", "\t\t\t\t\tadd_vegetables_inp_ingredients\n", "\t\t\t\t\tadd_vegetables_inp_crust\n", "\t\t\t\t\t\tmake_crust_out\n", "\t\t\t\t\t\t\tmake_crust\n", "\t\t\t\t\t\t\t\tmake_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_wheat_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_wheat_flour\n" ] } ], "source": [ "bake_stuffed_crust_out.get_source_tree().render()" ] }, { "cell_type": "markdown", "id": "0ffb0525-95ff-4e8c-afd8-9acd309215c3", "metadata": {}, "source": [ "Here we demonstrate how mutual exclusion is inherited: `Stuffed` crust is not directly exclusive to `Thin` crust, but `Regular` and `Thin` are exclusive and `Stuffed` inherits from `Regular`.\n", "\n", "We have additionally required that the crust be made of `Wheat`, which precludes the use of `make_gluten_free_crust`.\n", "\n", "Thus, there's lots of choices for topping ingredients on this pizza, but only the generic `make_crust` function has the necessary flexibility to satisfy our crust demands." ] }, { "cell_type": "code", "execution_count": 8, "id": "038d84ae-d0f1-44a7-8849-9920d35747aa", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "bake_dietary_restrictions_out\n", "\tbake_dietary_restrictions\n", "\t\tbake_dietary_restrictions_inp\n", "\t\t\tadd_vegetables_out\n", "\t\t\t\tadd_vegetables\n", "\t\t\t\t\tadd_vegetables_inp_ingredients\n", "\t\t\t\t\tadd_vegetables_inp_crust\n", "\t\t\t\t\t\tmake_crust_out\n", "\t\t\t\t\t\t\tmake_crust\n", "\t\t\t\t\t\t\t\tmake_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_corn_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_corn_flour\n", "\t\t\t\t\t\tmake_gluten_free_crust_out\n", "\t\t\t\t\t\t\tmake_gluten_free_crust\n", "\t\t\t\t\t\t\t\tmake_gluten_free_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_corn_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_corn_flour\n", "\t\t\t\t\t\tmake_thin_crust_out\n", "\t\t\t\t\t\t\tmake_thin_crust\n", "\t\t\t\t\t\t\t\tmake_thin_crust_inp_flour\n", "\t\t\t\t\t\t\t\t\tbuy_corn_flour_out\n", "\t\t\t\t\t\t\t\t\t\tbuy_corn_flour\n" ] } ], "source": [ "bake_dietary_restrictions_out.get_source_tree().render()" ] }, { "cell_type": "markdown", "id": "516037d5-293d-470c-b624-cdff5d889b48", "metadata": {}, "source": [ "Finally, we show how `transitive_requirements` pass requirements upstream. In this case `add_vegetables` -- our only option for ingredients since we've also required `Vegetarian` -- has no use for a `Flour` requirement, but we know it is possible to use this upstream so we have registered it as transtive. Later, we see that we _must_ buy only `Corn` flour to bake this pizza!\n", "\n", "As an experiment, you can go back and remove the transitive requirement and re-run the notebook. You should then see that this workflow terminates much earlier, because no workflow elements can be found that satisfy all the requirements!" ] }, { "cell_type": "markdown", "id": "cf147f17-6981-4480-a014-097c08cc6ce6", "metadata": {}, "source": [ "In the last two examples, we see that we get any workflow element who is _flexible enough_ that it _could_ satisfy our requirements. Alternatively, we could have written the path building to demand that upstream elements are only suggested if they are _more specific_ so they are _guaranteed_ to satisft requirements. It's possible we will switch to this second paradigm in future versions of `pyiron_ontology`, but knowing which is more useful will require building up more experience." ] }, { "cell_type": "code", "execution_count": null, "id": "6ad29dfc-5e92-4ee2-ac7c-eaca646ad66e", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.0" } }, "nbformat": 4, "nbformat_minor": 5 }