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

Rename classes #751

Merged
merged 30 commits into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b77952d
Rename Node to Function
liamhuber Jun 28, 2023
cf03a52
Rename FastNode to Fast
liamhuber Jun 28, 2023
8be1263
Rename SingleValueNode to SingleValue
liamhuber Jun 28, 2023
3751e2d
Update test class names
liamhuber Jun 28, 2023
1b049f7
Rename the decorator
liamhuber Jun 28, 2023
0a08a9f
Update docstrings
liamhuber Jun 28, 2023
2c44a10
Rename IsNodal to Node
liamhuber Jun 28, 2023
3fb23b3
Update the demo notebook
liamhuber Jun 28, 2023
50062d5
Merge branch 'fix_type_hints' into rename_classes
liamhuber Jun 28, 2023
ab8de4b
Black
liamhuber Jun 28, 2023
a98f4dd
Update docs
liamhuber Jun 28, 2023
f11424c
Introduce NotData as a default for data channels
liamhuber Jun 29, 2023
622a6a5
Update node tests that were expecting a None default
liamhuber Jun 29, 2023
bb4ff2f
Don't override the data channel default with None
liamhuber Jun 29, 2023
86173f6
Test more than just implementation
liamhuber Jun 29, 2023
17f9020
Update the example notebook
liamhuber Jun 29, 2023
b6d16b8
Update docstring
liamhuber Jun 29, 2023
3608cd6
Fix typo
liamhuber Jun 29, 2023
c85afdd
Make fast the default behaviour for function nodes
liamhuber Jun 29, 2023
d23a83e
Update the docstring
liamhuber Jun 29, 2023
76c5ab5
Remove unnecessary specification of the defaults
liamhuber Jun 29, 2023
cdd8cd9
Reparent SingleValue directly onto Function
liamhuber Jun 29, 2023
d36060c
Replace the Fast node with a Slow node now that fast is default
liamhuber Jun 29, 2023
4158fd3
Make atomistic calculation nodes slow
liamhuber Jun 29, 2023
03a011f
Expose the other functions on the node adder
liamhuber Jun 29, 2023
95eb3f5
Update the example notebook
liamhuber Jun 29, 2023
f825b67
Merge pull request #753 from pyiron/move_docstrings
samwaseda Jun 30, 2023
e44feab
Make NotData checking property private
Jun 30, 2023
357592e
Merge pull request #755 from pyiron/fast_by_default
liamhuber Jun 30, 2023
bdbbe4f
Merge pull request #754 from pyiron/data_channel_not_data
liamhuber Jun 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
279 changes: 160 additions & 119 deletions notebooks/workflow_example.ipynb

Large diffs are not rendered by default.

43 changes: 30 additions & 13 deletions pyiron_contrib/workflow/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
)

if typing.TYPE_CHECKING:
from pyiron_contrib.workflow.is_nodal import IsNodal
from pyiron_contrib.workflow.node import Node


class Channel(HasChannel, HasToDict, ABC):
Expand All @@ -51,26 +51,26 @@ class Channel(HasChannel, HasToDict, ABC):

Attributes:
label (str): The name of the channel.
node (pyiron_contrib.workflow.is_nodal.IsNodal): The node to which the channel
node (pyiron_contrib.workflow.node.Node): The node to which the channel
belongs.
connections (list[Channel]): Other channels to which this channel is connected.
"""

def __init__(
self,
label: str,
node: IsNodal,
node: Node,
):
"""
Make a new channel.

Args:
label (str): A name for the channel.
node (pyiron_contrib.workflow.is_nodal.IsNodal): The node to which the
node (pyiron_contrib.workflow.node.Node): The node to which the
channel belongs.
"""
self.label: str = label
self.node: IsNodal = node
self.node: Node = node
self.connections: list[Channel] = []

@abstractmethod
Expand Down Expand Up @@ -134,6 +134,15 @@ def to_dict(self) -> dict:
}


class NotData:
"""
This class exists purely to initialize data channel values where no default value
is provided; it lets the channel know that it has _no data in it_ and thus should
not identify as ready.
"""
pass


class DataChannel(Channel, ABC):
"""
Data channels control the flow of data on the graph.
Expand Down Expand Up @@ -170,6 +179,10 @@ class DataChannel(Channel, ABC):
E.g. `Literal[1, 2]` is as or more specific that both `Literal[1, 2]` and
`Literal[1, 2, "three"]`.

The data `value` will initialize to an instance of `NotData` by default.
The channel will identify as `ready` when the value is _not_ an instance of
`NotData`, and when the value conforms to type hints (if any).

Warning:
Type hinting in python is quite complex, and determining when a hint is
"more specific" can be tricky. For instance, in python 3.11 you can now type
Expand All @@ -181,8 +194,8 @@ class DataChannel(Channel, ABC):
def __init__(
self,
label: str,
node: IsNodal,
default: typing.Optional[typing.Any] = None,
node: Node,
default: typing.Optional[typing.Any] = NotData,
type_hint: typing.Optional[typing.Any] = None,
):
super().__init__(label=label, node=node)
Expand All @@ -199,9 +212,13 @@ def ready(self) -> bool:
(bool): Whether the value matches the type hint.
"""
if self.type_hint is not None:
return valid_value(self.value, self.type_hint)
return self._value_is_data and valid_value(self.value, self.type_hint)
else:
return True
return self._value_is_data

@property
def _value_is_data(self):
return self.value is not NotData

def update(self, value) -> None:
"""
Expand Down Expand Up @@ -313,8 +330,8 @@ class InputData(DataChannel):
def __init__(
self,
label: str,
node: IsNodal,
default: typing.Optional[typing.Any] = None,
node: Node,
default: typing.Optional[typing.Any] = NotData,
type_hint: typing.Optional[typing.Any] = None,
strict_connections: bool = True,
):
Expand Down Expand Up @@ -448,15 +465,15 @@ class InputSignal(SignalChannel):
def __init__(
self,
label: str,
node: IsNodal,
node: Node,
callback: callable,
):
"""
Make a new input signal channel.

Args:
label (str): A name for the channel.
node (pyiron_contrib.workflow.is_nodal.IsNodal): The node to which the
node (pyiron_contrib.workflow.node.Node): The node to which the
channel belongs.
callback (callable): An argument-free callback to invoke when calling this
object.
Expand Down
56 changes: 30 additions & 26 deletions pyiron_contrib/workflow/composite.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
from typing import Optional
from warnings import warn

from pyiron_contrib.workflow.is_nodal import IsNodal
from pyiron_contrib.workflow.node import Node, node, fast_node, single_value_node
from pyiron_contrib.workflow.node import Node
from pyiron_contrib.workflow.function import (
Function, SingleValue, Slow, function_node, slow_node, single_value_node
)
from pyiron_contrib.workflow.node_library import atomistics, standard
from pyiron_contrib.workflow.node_library.package import NodePackage
from pyiron_contrib.workflow.util import DotDict
Expand All @@ -20,12 +22,12 @@
class _NodeDecoratorAccess:
"""An intermediate container to store node-creating decorators as class methods."""

node = node
fast_node = fast_node
function_node = function_node
slow_node = slow_node
single_value_node = single_value_node


class Composite(IsNodal, ABC):
class Composite(Node, ABC):
"""
A base class for nodes that have internal structure -- i.e. they hold a sub-graph.

Expand All @@ -48,17 +50,17 @@ class Composite(IsNodal, ABC):
requirement is still passed on to children.

Attributes:
nodes (DotDict[pyiron_contrib.workflow.is_nodal,IsNodal]): The owned nodes that
nodes (DotDict[pyiron_contrib.workflow.node,Node]): The owned nodes that
form the composite subgraph.
strict_naming (bool): When true, repeated assignment of a new node to an
existing node label will raise an error, otherwise the label gets appended
with an index and the assignment proceeds. (Default is true: disallow assigning
to existing labels.)
add (NodeAdder): A tool for adding new nodes to this subgraph.
upstream_nodes (list[pyiron_contrib.workflow.is_nodal,IsNodal]): All the owned
upstream_nodes (list[pyiron_contrib.workflow.node,Node]): All the owned
nodes that have output connections but no input connections, i.e. the
upstream-most nodes.
starting_nodes (None | list[pyiron_contrib.workflow.is_nodal,IsNodal]): A subset
starting_nodes (None | list[pyiron_contrib.workflow.node,Node]): A subset
of the owned nodes to be used on running. (Default is None, running falls back
on using the `upstream_nodes`.)

Expand All @@ -81,9 +83,9 @@ def __init__(
):
super().__init__(*args, label=label, parent=parent, **kwargs)
self.strict_naming: bool = strict_naming
self.nodes: DotDict[str: IsNodal] = DotDict()
self.nodes: DotDict[str: Node] = DotDict()
self.add: NodeAdder = NodeAdder(self)
self.starting_nodes: None | list[IsNodal] = None
self.starting_nodes: None | list[Node] = None

def to_dict(self):
return {
Expand All @@ -92,7 +94,7 @@ def to_dict(self):
}

@property
def upstream_nodes(self) -> list[IsNodal]:
def upstream_nodes(self) -> list[Node]:
return [
node for node in self.nodes.values()
if node.outputs.connected and not node.inputs.connected
Expand All @@ -104,18 +106,18 @@ def on_run(self):
for node in starting_nodes:
node.run()

def add_node(self, node: IsNodal, label: Optional[str] = None) -> None:
def add_node(self, node: Node, label: Optional[str] = None) -> None:
"""
Assign a node to the parent. Optionally provide a new label for that node.

Args:
node (pyiron_contrib.workflow.is_nodal.IsNodal): The node to add.
node (pyiron_contrib.workflow.node.Node): The node to add.
label (Optional[str]): The label for this node.

Raises:
TypeError: If the
"""
if not isinstance(node, IsNodal):
if not isinstance(node, Node):
raise TypeError(
f"Only new node instances may be added, but got {type(node)}."
)
Expand All @@ -130,7 +132,7 @@ def add_node(self, node: IsNodal, label: Optional[str] = None) -> None:

def _get_unique_label(self, label):
if label in self.__dir__():
if isinstance(getattr(self, label), IsNodal):
if isinstance(getattr(self, label), Node):
if self.strict_naming:
raise AttributeError(
f"{label} is already the label for a node. Please remove it "
Expand Down Expand Up @@ -158,15 +160,15 @@ def _add_suffix_to_label(self, label):
)
return new_label

def _ensure_node_has_no_other_parent(self, node: IsNodal):
def _ensure_node_has_no_other_parent(self, node: Node):
if node.parent is not None and node.parent is not self:
raise ValueError(
f"The node ({node.label}) already belongs to the parent "
f"{node.parent.label}. Please remove it there before trying to "
f"add it to this parent ({self.label})."
)

def _ensure_node_is_not_duplicated(self, node: IsNodal, label: str):
def _ensure_node_is_not_duplicated(self, node: Node, label: str):
if (
node.parent is self
and label != node.label
Expand All @@ -178,16 +180,16 @@ def _ensure_node_is_not_duplicated(self, node: IsNodal, label: str):
)
del self.nodes[node.label]

def remove(self, node: IsNodal | str):
if isinstance(node, IsNodal):
def remove(self, node: Node | str):
if isinstance(node, Node):
node.parent = None
node.disconnect()
del self.nodes[node.label]
else:
del self.nodes[node]

def __setattr__(self, label: str, node: IsNodal):
if isinstance(node, IsNodal):
def __setattr__(self, label: str, node: Node):
if isinstance(node, Node):
self.add_node(node, label=label)
else:
super().__setattr__(label, node)
Expand Down Expand Up @@ -225,18 +227,20 @@ def __init__(self, parent: Composite):
self.register_nodes("atomistics", *atomistics.nodes)
self.register_nodes("standard", *standard.nodes)

Node = Node
Function = Function
Slow = Slow
SingleValue = SingleValue

def __getattribute__(self, key):
value = super().__getattribute__(key)
if value == Node:
return partial(Node, parent=self._parent)
if value == Function:
return partial(Function, parent=self._parent)
return value

def __call__(self, node: IsNodal):
def __call__(self, node: Node):
return self._parent.add_node(node)

def register_nodes(self, domain: str, *nodes: list[type[IsNodal]]):
def register_nodes(self, domain: str, *nodes: list[type[Node]]):
"""
Add a list of node classes to be accessible for creation under the provided
domain name.
Expand Down
Loading