Conversation
|
@liamhuber, could you please provide some specification/clarification of whether the proposal applies to the GUI only (there I can easily agree) or whether it has an impact also on the backend (i.e., do you suggest that the node gets an extra property, e.g. node.output_node) for which I do not see the need. |
|
My understanding was that we really wanted the nodes themselves to have the additional output in the back-end model. In this way we can form a true connection between that output port and the "functional" nodes that take it. I agree that the front-end 100% needs this. We currently get away without it at the code-level, but I feel it still improves clarity there too. In particular, if we need it for the front end, it then makes the front- and back-end to be better aligned if we do it in both places. So if we want it in the front-end, the question for me is rather "why not?" do it also in the back-end. The receivers are responsible for making a copy, so there is no real computational cost, as this port is really just holding a reference to it's owner. As far as I can see, it will only improve clarity for the functional applications. |
|
Do you have a suggestion for the syntax on the back-end? It would really help me to have something concrete. Specifically, I would like to see that we do not require users to learn additional keywords/syntax. |
|
I think we would need to reserve a port name, e.g. every node would come with a For the GUI users there is no new syntax to learn, because you'd see the port in the GUI -- we could easily modify the displayed name to just "node" or whatever, and even give it a different colour despite the fact it is a normal edge type. For the code users, my opinion is that one new clearly defined name is easier to learn/remember than the concept that a data output port can conditionally be carrying its owning node if it is connected to a downstream port hinted |
|
For the GUI users I fully agree. I am also fine to not misuse the ports for getting the node, i.e. |
|
I'm totally fine with the |
|
I like the idea of extending the edge definition to port handles being None (to mark a node input or output). It is perfectly symmetric, i.e., works for inputs or outputs accepting a node and can be easily translated to the GUI view (where we have a separate port for the node passing). I also don't foresee any major issues in the graph module. I am not sure about your second statement regarding the ground truth. The ground truth is a graph representation consisting of nodes and edges. Explicit port labels occur only in the edges, not in the nodes. Thus, if we agree on the above extension of the edge definition, I don't see any issue. The beauty of the above extension of the edge definition is that we don't need a new class for this case and that it automatically connects to our present way of expressing the graph. |
|
What I mean by the ground truth is that I strongly advise the underlying model to use something like What I am really lacking at this point is understanding the special benefits of trying to do this by introducing a new concept, when we already have existing concepts that handle the task just fine? In both cases we wind up with the ability to pass entire nodes as data, and in both cases we are free to create syntactic sugar so this is handled like Attack 1) Adding a self-like output port to every nodeCons:
Implementation:
In-notebook demo of the conceptimport pyiron_nodes as pn
import pyiron_workflow as pwf
import pyiron_workflow.simple_workflow as sw
wf = pwf.Workflow("iter_wf")
wf.n = pn.math.Add(1, 2)
wf.n.outputs = sw.Data(
{
sw.PORT_LABEL: wf.n.outputs.data[sw.PORT_LABEL] + ["pyiron_node"],
sw.PORT_TYPE: wf.n.outputs.data[sw.PORT_LABEL] + [sw.Node],
sw.PORT_VALUE: wf.n.outputs.data[sw.PORT_LABEL] + [wf.n],
"ready": wf.n.outputs.data["ready"] + [False],
"node": wf.n.outputs.data["node"] + [wf.n],
},
attribute=sw.Port
)
wf.iter = pn.executors.IterNode(wf.n.outputs.pyiron_node, "y", [1, 2, 3], store=False)
wf.run()Above demos the node extension, and how the new channel is used @property
def n_out_labels(self):
return len(self.outputs.data[PORT_LABEL]) - ("pyiron_node" in self.outputs.data[PORT_LABEL])Attack 2) Using the node itselfCons:
Implementation: ??? I don't know. I'm confident it is possible, but the path is not immediately clear to me. |
|
Thanks for summarizing your thoughts and arguments. I have a different point of view, and I feel that many of the arguments against Attack 2 can easily be overcome. Below are some thoughts:
|
|
@JNmpi, I have attempted to reflect your post from yesterday in an update to the decision, please re-review and let me know if further changes are needed. |
Following our experiment with decisions for pyiron, here is a summary of part of our discussion today.
For me, the line for when to use one of these instead of a regular github issue is quite blurry. At the moment, I'm focusing on things where I feel there is some risk that I misunderstood the conversation, and/or where the discussion has some reasonably significant impact on architecture. @hickel, I guess these also nicely go towards your point about documenting our conversations.
@jan-janssen, I've pinged you as well for review here because I'm not actually trying to propose or make any new decision, but only to accurately represent what was already discussed and decided. For that purpose, I don't think there's a need to involve both you and Joerg, but if either signs off that it's a fair summary I think that should be sufficient.