### Run Workflow with Input Parameters Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Executes a defined workflow by providing input values to its nodes. This example shows how to pass parameters, such as `range__n=5`, to the workflow execution, triggering the computation graph. ```Python wf(range__n=5) ``` -------------------------------- ### Draw Workflow Graph Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Provides examples of how to visualize the structure of a pyiron workflow or a specific macro node within a workflow using the `.draw()` method. ```python wf2.draw(size=(10,10)) ``` ```python wf2.lined_email.draw(size=(10,10)) ``` -------------------------------- ### Accessing Execution Provenance Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Shows how to access the execution provenance of a workflow, specifically the order in which nodes started executing and the order in which they finished executing. ```python macro_with_for_loops.provenance_by_execution ``` ```python macro_with_for_loops.iter_add.provenance_by_completion ``` -------------------------------- ### Package Distribution to PyPI and Anaconda Source: https://github.com/pyiron/pyiron_workflow/blob/main/CONTRIBUTING.rst Details how pyiron packages are pushed to PyPI (pip) and Anaconda.org. It includes examples of installation commands for both prerelease and official releases. ```shell # Installing prerelease versions with pip pip install --pre pyiron # Installing prerelease versions with conda conda install -c pyiron pyiron # Installing official releases via conda-forge conda install -c conda-forge pyiron ``` -------------------------------- ### Instantiate and Run a Macro Workflow Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Shows how to instantiate a workflow (`wf2`) and assign the `EmailWithLineNumbers` macro to a node within it. It then demonstrates how to provide input to the workflow and execute it. ```python wf2 = Workflow("spam_template") wf2.name = Workflow.create.std.UserInput() wf2.name.inputs.user_input.type_hint = str wf2.lined_email = EmailWithLineNumbers( recipient=wf2.name, body="You may have won some free beer! Please send your credit card number.", sender="Elsinore Brewery", salutation="Hurry, act fast!", ) wf2(name__user_input="Bob McKenzie") ``` -------------------------------- ### Import Workflow Class Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Imports the main `Workflow` class from the `pyiron_workflow` library, which serves as the primary entry point for creating and managing workflow nodes. ```python from pyiron_workflow import Workflow ``` -------------------------------- ### Access Node Inputs and Outputs Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Illustrates how node objects expose input and output channels. Inputs can be set programmatically, and output values can be retrieved after the node has been run. ```python node.inputs.x = 0 node.run() node.outputs.y.value ``` -------------------------------- ### Handle Workflow Failure with Recovery Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Sets up a workflow that is expected to raise an exception during execution. The `try-except` block catches the error to prevent the notebook from crashing. ```python wf = Workflow("will_fail")wf.some_number = Workflow.create.std.UserInput(1)wf.some_string = Workflow.create.std.UserInput("two")wf.addition = wf.some_number + wf.some_string try: wf() except Exception as e: print(e) print( "\nNormally this would have crashed our cell, " "but we want the notebook to keep running past it" ) ``` -------------------------------- ### Install pyiron_workflow Source: https://github.com/pyiron/pyiron_workflow/blob/main/docs/README.md Installs the pyiron_workflow package from the conda-forge channel. Additional packages for HPC execution are available under optional dependencies. ```shell conda install -c conda-forge pyiron_workflow ``` -------------------------------- ### Build Computational Graph Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Explains how to explicitly build a computational graph by connecting nodes. Node outputs are assigned to the inputs of other nodes, defining the data flow. ```python n1 = AddOne() n2 = AddOne() n3 = AddOne() n2.inputs.x = n1.outputs.y n3.inputs.x = n2.outputs.y n1.inputs.x = 0 n3() ``` -------------------------------- ### Node with Syntactic Sugar Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Shows an equivalent way to build a computational graph using syntactic sugar, where node inputs can be set directly during instantiation or by passing node objects. ```python n1 = AddOne(x=0) n2 = AddOne(x=n1) n3 = AddOne(x=n2) n3() ``` -------------------------------- ### Git Tagging for Releases Source: https://github.com/pyiron/pyiron_workflow/blob/main/CONTRIBUTING.rst Demonstrates the Git tagging convention used for pyiron release management. It shows the format for tag prefixes and example release tags. ```git https://git-scm.com/book/en/v2/Git-Basics-Tagging The tag format consists of a tag_prefix (-) and the release version, for example:: pyiron-0.2.0 ``` -------------------------------- ### Run Node Directly Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Shows how to execute a created node object by calling it like a regular Python function, passing input arguments directly. This is a convenient way to test or run individual nodes. ```python node(42) ``` -------------------------------- ### Python Workflow Parallelization Example Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Demonstrates parallel execution of a pyiron workflow using concurrent.futures.ThreadPoolExecutor. It shows how workflow nodes can be assigned executors to run concurrently, highlighting the importance of executor choice for shared Python objects and the limitations with ProcessPoolExecutor. ```python from concurrent import futures @Workflow.wrap.as_function_node def Report(t1, t2, t3): tmax = max(t1, t2, t3) print("LONGEST", tmax) return tmax wf = Workflow("sleepy") wf.t_sleep = Workflow.create.std.UserInput(2) wf.a1 = Workflow.create.std.Sleep(wf.t_sleep) wf.a2 = Workflow.create.std.Sleep(wf.t_sleep) wf.a3 = Workflow.create.std.Sleep(wf.t_sleep) wf.midway = Report(wf.a1, wf.a2, wf.a3) wf.b1 = Workflow.create.std.Sleep(wf.midway) wf.b2 = Workflow.create.std.Sleep(wf.midway) wf.b3 = Workflow.create.std.Sleep(wf.midway) wf.end = Report(wf.b1, wf.b2, wf.b3) from time import time t0 = time() with futures.ThreadPoolExecutor(max_workers=3) as exe: for n in wf: if n.label not in ["t_sleep", "midway", "finally"]: n.executor = exe wf() print("Total runtime", time() - t0) ``` -------------------------------- ### Define Custom Function Node with Multiple Outputs Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Demonstrates how to define a custom function node using the `@Workflow.wrap.as_function_node` decorator. This example shows how to specify multiple return values and assign names to them, which are then exposed as output channels. ```Python from pyiron_workflow import Workflow @Workflow.wrap.as_function_node("range", "length") def Range(n: int) -> tuple[list[int], int]: """ Two outputs is silly overkill, but just to demonstrate how Function nodes work """ r = range(n) return list(r), len(r) wf = Workflow("my_workflow") wf.range = Range() wf.last_square = wf.range.outputs.range[-1]**2 ``` -------------------------------- ### Conda Environment Setup for pyiron Source: https://github.com/pyiron/pyiron_workflow/blob/main/CONTRIBUTING.rst Commands to update, activate, and install conda build for pyiron development. This prepares the local environment for building and developing pyiron. ```shell conda env update --name pyiron_dev --file pyiron/.ci_support/environment.yml conda activate pyiron_dev conda install conda-build conda develop pyiron ``` -------------------------------- ### List Workflow Save Files Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Iterates through the directory where a workflow is saved and prints the names of the files. Useful for inspecting saved workflow components. ```python for item in wf2.as_path().iterdir(): print(item) ``` -------------------------------- ### Visualize Workflow Graph Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Shows how to visualize the structure and connections of a pyiron_workflow graph. The `draw` method generates a visual representation, illustrating nodes, their inputs/outputs, and dynamically injected operations. ```Python wf.draw(size=(10,10)) ``` -------------------------------- ### Reload Workflow by Label Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Reloads a previously saved pyiron workflow using its label. It then accesses and displays the workflow's outputs. ```python wf2_reloaded = Workflow(wf2.label) wf2_reloaded.outputs.to_value_dict() ``` -------------------------------- ### Inspect Checkpointed Workflow Reload Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Reloads a workflow that was saved using checkpoints and prints the label and output values of each node, showing the state at the time of the last checkpoint. ```python reload_checkpointed = Workflow(wf.label) for n in reload_checkpointed: print(n.label, n.outputs.to_value_dict()) ``` -------------------------------- ### Wrap Function as Node Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Demonstrates wrapping a simple Python function `AddOne` using the `@Workflow.wrap.as_function_node` decorator. This converts the function into a pyiron_workflow node that can be part of a computational graph. ```python @Workflow.wrap.as_function_node def AddOne(x): y = x + 1 return y node = AddOne() ``` -------------------------------- ### Load Failed Node from Recovery File Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Reloads a workflow from a 'recovery' file created when a node failed. It then checks the 'failed' status and visualizes the workflow graph. ```python reloaded = Workflow("will_fail") reloaded.load(filename=reloaded.as_path().joinpath("recovery")) print("Failed?", reloaded.failed) reloaded.draw(size=(10, 10)) ``` -------------------------------- ### Macro Return Value: Non-Data Channel Error Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Illustrates an error case where a macro-defining function returns a non-data-channel value, which is not allowed. The example shows how to catch this `AttributeError` and explains the underlying issue. ```python try: @Workflow.wrap.as_macro_node("x", "y") def Foo(self, a): return a + 1, 6 Foo() except AttributeError: print("Returns a non-data-channel") ``` -------------------------------- ### For-Loops with List-like Output Channels Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Demonstrates creating `for_node` instances that output data as individual list-like channels instead of a DataFrame. This is useful for connecting loop outputs to other nodes within the workflow graph. ```python wf = Workflow("listlike_for_loop")wf.first_loop = Workflow.create.for_node( Workflow.create.std.Add, iter_on="other", obj=1, other=[1, 2, 4], output_as_dataframe=False, )wf.second_loop = Workflow.create.for_node( Workflow.create.std.Multiply, iter_on="other", obj=2, other=wf.first_loop.outputs.add, output_as_dataframe=False, )wf() ``` -------------------------------- ### Checkpoint Workflow Execution Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Configures a node to trigger a save of the parent workflow graph whenever the node finishes running, using a specified backend like 'pickle'. ```python wf = Workflow("checkpointed")wf.a = Workflow.create.std.UserInput(42)wf.b = wf.a + 1wf.c = wf.b + 1 wf.b.checkpoint = "pickle"wf() ``` -------------------------------- ### Specify Output Channel Name Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Demonstrates how to explicitly specify the name of an output channel using the decorator argument. This is useful for clarity or when the return statement variable name is not ideal. ```python @Workflow.wrap.as_function_node("y") def AddOne(x: int) -> int: return x + 1 AddOne.preview_io() ``` -------------------------------- ### Travis CI Configuration for Python Packages Source: https://github.com/pyiron/pyiron_workflow/blob/main/CONTRIBUTING.rst Shows an example of a Travis CI configuration file (.travis.yml) used for building and testing pyiron's pure Python packages, specifically highlighting the use of Python 3.7. ```yaml https://github.com/pyiron/pyiron_base/blob/master/.travis.yml ``` -------------------------------- ### Debug pyiron Tests with pdb Source: https://github.com/pyiron/pyiron_workflow/blob/main/CONTRIBUTING.rst Example of combining the pdb debugger with the unittest module to facilitate interactive debugging of pyiron tests. ```python python -m pdb -m unittest ... ``` -------------------------------- ### Demonstrate Type Hint Enforcement and Error Handling Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Illustrates the type checking capabilities of pyiron_workflow. When type hints are defined for node inputs, the library enforces them, raising a `TypeError` if an incompatible data type is provided. ```Python try: wf.range.inputs.n = 5.5 except TypeError as e: message = e.args[0] print(message) ``` -------------------------------- ### Clean Up Checkpointed Workflow Storage Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Deletes the storage files associated with a checkpointed workflow. ```python # Lastly, just clean up after ourselves reload_checkpointed.delete_storage() ``` -------------------------------- ### Zipped For-Loops with `zip_on` and DataFrame Output Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Illustrates creating a `for_node` for zipped iteration using `zip_on`. This groups corresponding elements from multiple input lists. The `output_as_dataframe` flag packages the results into a pandas DataFrame. ```python n = Workflow.create.for_node( Workflow.create.std.Add, zip_on=("obj", "other"), obj=[1, 2, 3], other=[4, 5, 6], output_as_dataframe=True ) out = n() out["df"] ``` -------------------------------- ### Macro Definition: Mixing Function and Macro Logic Error Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Highlights a common mistake where standard Python operations (like `str()`) are used directly within a macro definition, leading to incorrect behavior or errors because they are not treated as data channels. This example shows the error scenario. ```python try: @Workflow.wrap.as_macro_node def Foo(self, a): self.number = a + 1 self.string = str(self.number) return self.string Foo() except AttributeError: print("De-data-channels `a`") ``` -------------------------------- ### Clean Up Recovery Save File Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Deletes the specific 'recovery' file created for a failed workflow node. ```python # Clean up reloaded.delete_storage(filename=reloaded.as_path().joinpath("recovery")) ``` -------------------------------- ### Add Un-parented Nodes to Workflow Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Explains how to add pre-existing, un-parented nodes to a workflow after its initial creation. This allows for dynamic graph construction and integration of independently developed components. ```Python some_node = Range(n=5) biggest_of_some_node = some_node.outputs.range[-1] some_other_node = Range(n=biggest_of_some_node) some_other_node.pull() ``` -------------------------------- ### Nested For-Loops with `iter_on` Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Demonstrates creating a `for_node` for nested iteration. The `iter_on` parameter specifies which inputs should be iterated over, allowing for combinations of input values across multiple parameters. ```python n = Workflow.create.for_node( Workflow.create.std.Add, iter_on=("obj", "other"), obj=[1, 2], other=[3, 4] ) out = n() out ``` -------------------------------- ### Save Workflow State Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Saves the current state of a pyiron workflow to a canonical path based on its label. This allows for preserving data between sessions. ```python wf2.save() ``` -------------------------------- ### Nest Function Nodes Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Demonstrates nesting workflow nodes by passing one node's output as another's input, creating a chain of operations. This is syntactic sugar for building a graph. ```python calculation = AddOne(AddOne(AddOne(2))) calculation() ``` -------------------------------- ### Repair and Rerun Workflow After Failure Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Removes the failed node from the reloaded workflow, adds a new node, and then reruns the workflow. The `rerun=True` flag resets the status to allow execution. ```python reloaded.remove_child(reloaded.addition) reloaded.stringify = Workflow.create.std.String(reloaded.some_number) reloaded.addition = reloaded.stringify + reloaded.some_string reloaded.run(rerun=True) # Re-set the status with `rerun` to be ready to run again ``` -------------------------------- ### Macro Node with Injection Example Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Illustrates creating a macro node (`TheFifthElement`) that utilizes node injection for accessing data, performing arithmetic operations with injected operators, and calling injected methods. It shows how to define inputs, process data, and return results, demonstrating the flexibility of the injection mechanism. ```python @pwf.as_macro_node def TheFifthElement( self, exactly_five: tuple[int, int, int, int, int], mod: int = 10, extra: int = 5, target: int = 0, ) -> tuple[bool, float]: self.fifth_element = exactly_five[4] # Access injection self.transformed = (self.fifth_element + extra) % mod # Operator injection self.matches = self.transformed.eq(target) # Method injection as_float = self.transformed.float() return self.matches, as_float five = TheFifthElement((1, 2, 3, 4, 5)) for n in five: print(f"{n.label} <{n.__class__.__name__}>") five() ``` -------------------------------- ### Configuring Execution Flow with Signal Connections Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Demonstrates how to manually control workflow execution order using signal connections between nodes. This example sets up `run` and `accumulate_and_run` signals for a `Verbose` node to manage dependencies. ```python @pwf.as_function_node def Verbose(x, y, z): print(x, y, z) return z wf = pwf.Workflow("run_control") wf.start = Verbose("", "", "start") wf.immediate = Verbose(wf.start, "", "immediate") wf.collecting = Verbose(wf.start, wf.immediate, "collecting") wf.immediate.signals.input.run = wf.start.signals.output.ran wf.collecting.signals.input.accumulate_and_run = wf.start.signals.output.ran wf.collecting.signals.input.accumulate_and_run = wf.immediate.signals.output.ran wf.starting_nodes = [wf.start] # If _any_ signals are manually set, then _all_ signals and starting nodes must be set # There is no mixing and matching wf.automate_execution = False # This flag is needed only for Workflows, but not in Macro definitions wf() ``` -------------------------------- ### Check if Workflow Save Path Exists Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Checks if the directory designated for saving a workflow's state exists on the file system. ```python wf2.as_path().exists() ``` -------------------------------- ### Dataclass Node Definition Example Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Demonstrates the creation of dataclass nodes using the `@pwf.as_dataclass_node` decorator. It shows how to define a base dataclass (`Car`) with default values and how to create a derived dataclass (`Coupe`) that inherits from the base dataclass's definition, illustrating input grouping and composition. ```python @pwf.as_dataclass_node class Car: color: str doors: int wheels: int = 4 @pwf.as_dataclass_node class Coupe(Car.dataclass): doors: int = 2 spoiler: bool = False # Note: Inheritance with dataclasses can be tricky, particularly with regards to the order of # default and non-default parameters. In general, composition is preferable over inheritance and # extension. So this example is somewhat questionable python, but we're just trying to show ``` -------------------------------- ### Disable Workflow Auto-loading Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Creates a new Workflow instance with auto-loading disabled, preventing it from automatically loading a save file if one exists with the given label. ```python wf_not_2 = Workflow(wf2.label, autoload=None) wf_not_2.outputs.to_value_dict() ``` -------------------------------- ### Handle Uniqueness of Input Connections Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Demonstrates the constraint that each data input can only have one incoming connection. The example shows an exception being raised when attempting to connect a second source to an already connected input. ```python source1 = pwf.std.UserInput(label="source1") source2 = pwf.std.UserInput(label="source2") receiver = pwf.std.UserInput(42, label="receiver") receiver.inputs.user_input.connect(source1.outputs.user_input) try: receiver.inputs.user_input.connect(source2.outputs.user_input) except Exception as e: print(e) ``` -------------------------------- ### Define Function and Macro Nodes Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Demonstrates defining a simple function node `Print` and a macro node `EmailWithLineNumbers` using pyiron's `@Workflow.wrap` decorators. The `EmailWithLineNumbers` macro composes multiple `Print` function nodes to structure an email with line numbers. ```python @Workflow.wrap.as_function_node("next_line") def Print(message: str, line_number: int = 0) -> int: print(line_number, message) return line_number + 1 @Workflow.wrap.as_macro_node("n_lines") def EmailWithLineNumbers( self, recipient: str, body: str, sender: str, honourific: str = "Dear ", salutation: str = "Sincerely,", ): # self.greeting = Workflow.create.std.Add(honourific, " ") + recipient self.greet = Print(honourific + recipient + ",") self.communicate = Print(body, line_number=self.greet) self.conclude = Print(salutation, line_number=self.communicate) self.from_ = Print(sender, line_number=self.conclude) return self.from_ ``` -------------------------------- ### Macro Definition: Correcting Mixed Logic with Function Nodes Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Provides the correct approach to handle operations that should occur at runtime within a macro. It involves extracting such operations into separate function nodes and then calling these function nodes from within the macro definition. ```python @Workflow.wrap.as_function_node def Stringify(a): as_string = str(a) return as_string @Workflow.wrap.as_macro_node def Foo(self, a): self.number = a + 1 self.string = Stringify(self.number) return self.string Foo()(1) ``` -------------------------------- ### For-Loops within Macros and Executor Assignment Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Shows how to define a macro that contains `for_node` instances. It also demonstrates assigning a specific executor (e.g., `ThreadPoolExecutor`) to the body nodes of a loop, controlling how the iterations are executed. ```python @Workflow.wrap.as_macro_node def InternallyIterates(self, data: list[int], start: int = 0): self.iter_add = Workflow.create.for_node( body_node_class=Workflow.create.std.Add, iter_on="other", obj=start, other=data ) self.zip_add = Workflow.create.for_node( Workflow.create.std.Add, zip_on=("obj", "other"), obj=data, other=data ) return self.iter_add, self.zip_add macro_with_for_loops = InternallyIterates() with futures.ThreadPoolExecutor(max_workers=1) as exe: macro_with_for_loops.iter_add.body_node_executor = exe out = macro_with_for_loops(data=[1, 2, 3]) out ``` -------------------------------- ### Replace Child Node Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Demonstrates replacing an existing child node with a new one. The replacement node must have compatible input/output (IO) characteristics. The example shows updating a node and verifying the change. ```python replacement = pwf.std.UserInput(42) print(wf.UserInput0.inputs.user_input.value) wf.replace_child(wf.UserInput0, replacement) print(wf.UserInput0.inputs.user_input.value) ``` -------------------------------- ### Convert Inputs to Dictionary using pyiron_workflow Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Shows the usage of `inputs_to_dict` from `pyiron_workflow.api` to create a node that processes inputs into a dictionary. The example defines an input specification with default values and types, then demonstrates previewing the I/O structure and passing a single argument to the node. ```python i2d = pwf.api.inputs_to_dict( input_specification={ "x": (None, pwf.api.NOT_DATA), "y": (int, 42) } ) print(i2d.preview_io()) i2d("foobar") ``` -------------------------------- ### Define Composable Macro Node with Multiple Outputs Source: https://github.com/pyiron/pyiron_workflow/blob/main/docs/README.md Demonstrates defining a reusable macro node using the `@Workflow.wrap.as_macro_node` decorator. This example shows how a macro can return multiple distinct outputs, which can then be composed into larger workflows. ```python >>> @Workflow.wrap.as_macro_node ... def Composition(self, greeting): ... self.compose = Combined(greeting=greeting) ... self.simple = greeting + " there" ... return self.compose, self.simple >>> >>> composed = Composition() >>> composed(greeting="Hi") {'compose': 'Hi You and Hi World', 'simple': 'Hi there'} ``` -------------------------------- ### Convert List to Outputs using pyiron_workflow Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Illustrates the `list_to_outputs` utility from `pyiron_workflow.api`. This function creates a node that takes a list as input and distributes its elements as individual outputs. The example shows how to initialize it with a count and pass the output of `inputs_to_list` to it. ```python l2o = pwf.api.list_to_outputs(5) l2o(i2l) ``` -------------------------------- ### Get Workflow Dictionary Representation Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Illustrates how to obtain a dictionary representation of a workflow, including its nodes and edges. The 'nodes' subdictionary maps labels to instances, while 'edges' details data and signal connections. ```python wf = pwf.Workflow("graph_as_dict")wf.inp = pwf.std.UserInput(42)wf.a = wf.inp + 2wf.b = wf.inp + 4wf.out = wf.a * wf.bwf.graph_as_dict ``` -------------------------------- ### Convert Inputs to List using pyiron_workflow Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Demonstrates the `inputs_to_list` utility from `pyiron_workflow.api`. This function creates a node that collects a specified number of arguments into a list. The example shows how to initialize the node with a count and then pass various data types as arguments. ```python i2l = pwf.api.inputs_to_list(5) i2l(1, 2, 3, "four", "five") ``` -------------------------------- ### Macro Return Value: Correcting Non-Data Channel Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Presents the solution to the non-data-channel return error by wrapping the literal value (or computed value) as a node, ensuring it's treated as a data channel by the macro definition. ```python @Workflow.wrap.as_macro_node("x", "y") def Foo(self, a): self.six = Workflow.create.std.UserInput(6) return a + 1, self.six Foo()(a=1) ``` -------------------------------- ### Handle Naming Conflicts in Workflow Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Addresses how to manage naming collisions when adding multiple nodes to a workflow, especially when nodes are added post-facto. Setting `strict_naming=False` allows the workflow to automatically rename conflicting nodes. ```Python oh_no_I_need_a = Workflow( "post_facto", some_node, biggest_of_some_node, some_other_node, strict_naming=False ) oh_no_I_need_a.child_labels ``` -------------------------------- ### Automated Versioning with Versioneer Source: https://github.com/pyiron/pyiron_workflow/blob/main/CONTRIBUTING.rst Illustrates the configuration for automated versioning using the 'versioneer' tool, as specified in the setup.cfg file. ```ini https://github.com/pyiron/pyiron_base/blob/master/setup.cfg ``` -------------------------------- ### Demonstrating Mutability Issues in Pyiron Workflow Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Illustrates how mutating mutable input data, such as lists, within workflow nodes can lead to unexpected caching behavior and skipped executions. This example uses a `SayMyName` node with a mutable `collection` list to show the problem. ```python @pwf.as_function_node def SayMyName(name: str, collection: list[str]) -> tuple[str, list[str]]: collection.append(name) my_name_is = f"My name is {name}, and I've collected {collection}" print(my_name_is) return my_name_is, collection wf = pwf.Workflow("mutable_trouble") wf.a = SayMyName("Alice", []) wf.b = SayMyName("Bob", wf.a.outputs.collection) wf.c = SayMyName("Chandy", wf.b.outputs.collection) wf.d = SayMyName("Deng", wf.c.outputs.collection) wf() ``` -------------------------------- ### Convert Inputs to DataFrame using pyiron_workflow Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Demonstrates the `inputs_to_dataframe` utility from `pyiron_workflow.api`. This function is used to create a node that accepts multiple rows of input data, structured as dictionaries, and converts them into a pandas DataFrame. The example shows how to define the input structure and populate it with data. ```python i2df = pwf.api.inputs_to_dataframe(3) i2df( row_0 = {"strings": "a", "ints": 1, "types": int}, row_1 = {"strings": "b", "ints": 2, "types": float}, row_2 = {"strings": "c", "ints": 3, "types": str}, ) ``` -------------------------------- ### Build a Workflow with Nodes and Inputs Source: https://github.com/pyiron/pyiron_workflow/blob/main/docs/README.md Demonstrates constructing a computation graph using the `Workflow` object. It shows adding nodes, connecting them, and using `UserInput` nodes for dynamic input. The workflow execution returns a dictionary of results. ```python >>> wf = Workflow("readme") >>> wf.greeting = Workflow.create.std.UserInput("Hi") >>> wf.first = HelloWorld(greeting=wf.greeting) >>> wf.second = HelloWorld(greeting=wf.greeting) >>> wf.combined = wf.first + " and " + wf.second >>> wf() ``` -------------------------------- ### Delete Workflow Save Files Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Manually deletes all files associated with a workflow's saved state from its storage directory. ```python wf2.delete_storage() ``` -------------------------------- ### Disable Automatic Failure Recovery Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/quickstart.ipynb Disables the automatic saving of failed nodes by setting the node's `.recovery` attribute to `None`. ```python # This automatic saving can be disabled by setting the node attribute `.recovery` to `None`. ``` -------------------------------- ### Import and Run Re-wrapped Function Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Demonstrates how to import a re-wrapped function (like `JustMakeItGo`) from the current execution context and run it. This confirms the wrapping and execution process works as intended. ```Python # Assuming JustMakeItGo was defined in the same scope or imported # from __main__ import JustMakeItGo as ReimportedJMIG # For demonstration, we'll use the locally defined JustMakeItGo # assert(ReimportedJMIG is JustMakeItGo) # This node runs fine: jmig = JustMakeItGo() assert(jmig(5) is True) # Example with string input assert(jmig("hello") == "hello") # Example with negative input assert(jmig(-5) is False) ``` -------------------------------- ### Add pyiron to PYTHONPATH Source: https://github.com/pyiron/pyiron_workflow/blob/main/CONTRIBUTING.rst Exporting the pyiron checkout directory to the PYTHONPATH environment variable. This ensures Python uses the local development version over any system-wide installation. ```shell export PYTHONPATH="$HOME/software/pyiron:$PYTHONPATH" ``` -------------------------------- ### Preview Node IO Interface Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Shows how to inspect the input and output interface of a dynamically created node class. This allows users to understand the expected data types, labels, and default values before instantiating the node, leveraging the class-level IO definition. ```Python MyFunctionClass.preview_io() ``` -------------------------------- ### Instantiate and Call Function Node Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Illustrates instantiating a custom function node, both with default and positional arguments for its input. It also shows how to execute the node's logic and how to override input values using keyword arguments during execution. ```Python my_func1 = MyFunctionClass(label="default_input") my_func2 = MyFunctionClass(1, label="positional_input") my_func2() my_func2(x=100) ``` -------------------------------- ### Inspect OutputDataWithInjection Methods Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb This snippet demonstrates how to inspect and print the methods available on the `OutputDataWithInjection` class, excluding initialization and internal methods. It helps understand the injection capabilities provided by the framework. ```python from types import FunctionType from pyiron_workflow.mixin.injection import OutputDataWithInjection for k, v in OutputDataWithInjection.__dict__.items(): if ( isinstance(v, FunctionType) and k not in ["__init__", "_get_injection_label", "_node_injection"] ): print(k, v) ``` -------------------------------- ### Sphinx Documentation Building Source: https://github.com/pyiron/pyiron_workflow/blob/main/CONTRIBUTING.rst Mentions the use of Sphinx for automatically building documentation and specifies that manually created documentation should be added as .rst files in the pyiron/docs/source directory. ```rst Documentation is built automatically with `Sphinx`_; any manually created documentation should be added as a restructured text (.rst) file under pyiron/docs/source. ``` -------------------------------- ### Re-run Workflow with Dynamic Inputs Source: https://github.com/pyiron/pyiron_workflow/blob/main/docs/README.md Explains how to re-run a `Workflow` by providing new values for its inputs using keyword arguments, allowing for flexible parameter tuning and execution without modifying the workflow definition. ```python >>> wf(greeting__user_input="Hey", first__subject="you") ``` -------------------------------- ### Run pyiron Unit Tests Source: https://github.com/pyiron/pyiron_workflow/blob/main/CONTRIBUTING.rst Commands to discover and run pyiron's unit tests from the repository root. Supports running all tests, tests for specific modules, or individual test files. ```python python -m unittest discover tests python -m unittest discover tests/sphinx python -m unittest tests/sphinx/test_base.py ``` -------------------------------- ### Clone pyiron Repository for Development Source: https://github.com/pyiron/pyiron_workflow/blob/main/CONTRIBUTING.rst Instructions to clone the pyiron project from its GitHub repository. This is the initial step for setting up a local development environment to contribute to the project. ```Shell git clone https://github.com/pyiron/pyiron.git ``` -------------------------------- ### Clone pyiron Development Repository Source: https://github.com/pyiron/pyiron_workflow/blob/main/CONTRIBUTING.rst Steps to clone the pyiron repository into a local directory. This is the first step to using a development version of pyiron. ```shell mkdir -p ~/software cd ~/software git clone https://github.com/pyiron/pyiron.git ``` -------------------------------- ### Define and Execute a While Loop Workflow Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Illustrates the creation and execution of a workflow featuring a while loop. It shows how to define the test and body nodes, specify connections, and manage loop termination conditions and iteration limits. ```python wf = pwf.Workflow("my_while_loop")wf.x0 = pwf.std.UserInput(0)wf.limit = pwf.std.UserInput(5)wf.step = pwf.std.UserInput(2)wf.add_while = pwf.while_node( pwf.std.LessThan, # Test pwf.std.Add, # Body [("add", "obj")], # body-to-test [("add", "obj")], # body-to-body strict_condition_hint=False, # LessThan doesn't hint it's boolean return... test_obj=wf.x0, test_other=wf.limit, body_obj=wf.x0, body_other=wf.step )wf.xf = wf.add_while.outputs.add # While output maps to body output ``` ```python wf.draw(size=(10, 10)) ``` ```python wf() ``` ```python wf.add_while.child_labels ``` ```python wf.add_while.body_2.outputs.add.value ``` ```python wf.add_while.body_3.outputs.add.value ``` -------------------------------- ### Using Syntactic Sugar for Signal Connections Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Demonstrates a more concise way to define signal connections using the `>>` and `<<` operators. This syntactic sugar simplifies the process of wiring nodes for execution control. ```python wf = pwf.Workflow("sugar", automate_execution=False) wf.start = Verbose("", "", "start") wf.immediate = Verbose(wf.start, "", "immediate") wf.collecting = Verbose(wf.start, wf.immediate, "collecting") wf.start >> wf.immediate wf.collecting << (wf.start, wf.immediate) wf.starting_nodes = [wf.start] wf() ``` -------------------------------- ### Import pyiron_workflow Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Imports the pyiron_workflow library, typically aliased as pwf for convenience in defining and managing computational workflows. ```Python import pyiron_workflow as pwf ``` -------------------------------- ### Instantiate Child with Parent Workflow Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Demonstrates adding a child node by setting the `parent` argument during instantiation. It shows how to access the child labels after this operation, highlighting that the child becomes aware of its parent at instantiation. ```python pwf.std.UserInput(parent=wf) wf.child_labels ``` -------------------------------- ### Save and Load While Loop Workflows Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Demonstrates the persistence of while loop structures within pyiron_workflow by showing how to save (serialize) and load (deserialize) a workflow containing a while loop using the pickle module. ```python import pickle reloaded = pickle.loads(pickle.dumps(wf)) reloaded.outputs.to_value_dict() ``` -------------------------------- ### Define pyiron_workflow Macro Node Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Illustrates the creation of a macro node using `@pwf.as_macro_node`. Macros define a graph executed once at instantiation, using other nodes internally. ```python import pyiron_workflow as pwf # Assuming TimesTwo is defined as above # @pwf.as_function_node # def TimesTwo(x: int) -> int: # twox = 2 * x # return twox @pwf.as_macro_node def TwoCubed(self, x: int) -> int: self.two = TimesTwo(x) self.four = TimesTwo(self.two) self.eight = TimesTwo(self.four) return self.eight ``` -------------------------------- ### Workflow IO Mapping and Dynamic Naming Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Explains how `pyiron_workflow` workflows handle I/O, noting that it's mutable and re-created dynamically. It demonstrates how to access child node I/O directly through the workflow and how to customize the I/O channel names using `inputs_map` and `outputs_map` for explicit naming conventions. ```python wf = pwf.Workflow("no_wall") wf.child = pwf.std.UserInput() assert(wf.inputs.child__user_input is wf.child.inputs.user_input) wf.inputs_map = {"child__user_input": "inp"} wf.outputs_map = {"child__user_input": "out"} wf(inp="some data") ``` -------------------------------- ### Import NodeSlurmExecutor Source: https://github.com/pyiron/pyiron_workflow/blob/main/docs/README.md Demonstrates how to import the NodeSlurmExecutor class from the pyiron_workflow library, which is used for facilitating HPC execution. ```python from pyiron_workflow import NodeSlurmExecutor ``` -------------------------------- ### Load and Run Saved Job in a New Notebook Source: https://github.com/pyiron/pyiron_workflow/blob/main/CONTRIBUTING.rst Demonstrates loading a previously saved job using its ID in a new notebook environment and attempting to run it. This isolates the issue to the job execution itself, independent of the initial submission process. ```Python from pyiron import Project # we do not import pyiron_contrib here, becasue it should not be necessary pr = Project("second_notebook") reloaded_job = pr.load(98) # 98 is the job id of the previously saved job reloaded_job.run(run_again=True) ``` -------------------------------- ### Inspecting Node Inputs and Outputs Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Demonstrates how to inspect the input and output values of a node using the `to_value_dict()` method. This is useful for understanding the state of data within the workflow and debugging mutability issues. ```python wf.a.inputs.to_value_dict() ``` ```python wf.a.outputs.to_value_dict() ``` -------------------------------- ### Disable Strict Type Hints in pyiron_workflow Nodes Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Illustrates how to disable strict type checking for node inputs in `pyiron_workflow` using the `deactivate_strict_hints()` method. This allows passing incompatible data types, but the example warns that this can lead to runtime errors like `AttributeError` if not handled carefully, as demonstrated by attempting to run a node with invalid input after disabling hints. ```python repainted = Repaint() repainted.inputs.new_color = "light urple" repainted.deactivate_strict_hints() repainted.inputs.car = "a_string_not_a_Car" try: repainted.recovery = None # We know this will fail and don't care about a recovery file repainted.run() except AttributeError as e: print("AttributeError:", e) ``` -------------------------------- ### Define a Macro Node Source: https://github.com/pyiron/pyiron_workflow/blob/main/docs/README.md Illustrates how to package a computation graph into a reusable `Macro` node using the `@Workflow.wrap.as_macro_node` decorator. Macro nodes execute their graph definition once at instantiation, offering more control over exposed IO. ```python @Workflow.wrap.as_macro_node def Combined(wf, greeting="Hey", subject1="You", subject2="World"): wf.first = HelloWorld(greeting=greeting, subject=subject1) wf.second = HelloWorld(greeting=greeting, subject=subject2) wf.combined = wf.first + " and " + wf.second return wf.combined hello_macro = Combined() hello_macro(subject2="everyone") ``` -------------------------------- ### Create Custom Node via Subclassing Source: https://github.com/pyiron/pyiron_workflow/blob/main/notebooks/deepdive.ipynb Demonstrates creating a custom pyiron_workflow node by inheriting from `pwf.api.Function`. This approach allows for custom logic and output naming via `_output_labels` and methods like `is_bound`. ```Python import pyiron_workflow as pwf class MySubFunction(pwf.api.Function): @staticmethod def node_function(x: int = 42) -> tuple[int, int]: return x + 1, x - 1 _output_labels = ("plus", "minus") def is_bound(self, n) -> bool: if self.outputs.plus.value is not pwf.api.NOT_DATA: return self.outputs.plus.value > n and self.outputs.minus.value < n else: raise RuntimeError("Run the node first") my_subfunc = MySubFunction() try: my_subfunc.is_bound(33) except RuntimeError: print("Maybe our method depends on having output data") my_subfunc() assert(my_subfunc.is_bound(42.5) and not my_subfunc.is_bound(0)) ```