-
Notifications
You must be signed in to change notification settings - Fork 189
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
Implement new link types #2220
Implement new link types #2220
Conversation
bf4458c
to
c3ca6e4
Compare
With the new ORM hierarchy in place, the corresponding link types can be defined: CREATE: CalculationNode -> Data RETURN: WorkflowNode -> Data INPUT_CALC: Data -> CalculationNode INPUT_WORK: Data -> WorkflowNode CALL_CALC: WorkflowNode -> CalculationNode CALL_WORK: WorkflowNode -> WorkflowNode These rules are encoded in the `validate_link` function in `aiida.orm.utils.links`. This function is called from `add_link_from`, which will be renamed to `add_incoming`, and validate the triple (source, link, target) representing the addition of a link from a source node to a target node.
2de265a
to
51ff0bc
Compare
This is to align it with the recent implementation of `get_incoming` and `get_outgoing` to obtain all incoming and outgoing links, respectively.
51ff0bc
to
9db7997
Compare
aiida/orm/utils/links.py
Outdated
if not isinstance(source, type_source) or not isinstance(target, type_target): | ||
raise ValueError('cannot add a {} link from {} to {}'.format(link_type, type(source), type(target))) | ||
|
||
incoming = dict(target.get_inputs(node_type=type(source), link_type=link_type, also_labels=True)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use get_incoming
instead
aiida/orm/utils/links.py
Outdated
raise ValueError('node<{}> already has an incoming {} link'.format(target.uuid, link_type)) | ||
|
||
# If the indegree is `unique_label` than the pair (source, label) should be unique for the target node | ||
if type_degree == 'unique_label' and (source.uuid, link_label) in [(n.uuid, l) for l, n in incoming.items()]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider changing to just check the link labels for uniqueness at this point.
@@ -36,7 +39,7 @@ def test_status(self): | |||
"""Test the status command.""" | |||
calc = WorkChainNode().store() | |||
result = self.cli_runner.invoke(cmd_work.work_status, [str(calc.pk)]) | |||
self.assertIsNone(result.exception, result.output) | |||
self.assertIsNone(result.exception, ''.join(traceback.format_exception(*result.exc_info))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using self.assertClickResultNoException
|
||
# Even when the source node is different | ||
with self.assertRaises(ValueError): | ||
target.validate_incoming(source_two, LinkType.CREATE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a case where link label is different
@@ -280,7 +280,7 @@ def _add_dblink_from(self, src, label=None, link_type=LinkType.UNSPECIFIED): | |||
# | |||
# I am linking src->self; a loop would be created if a DbPath exists | |||
# already in the TC table from self to src | |||
if link_type is LinkType.CREATE or link_type is LinkType.INPUT: | |||
if link_type is LinkType.CREATE or link_type is LinkType.INPUT_CALC or link_type is LinkType.INPUT_WORK: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leave checks out from backend classes
""" | ||
super(CalculationNode, self).validate_incoming(source, link_type, link_label) | ||
from aiida.orm.utils.links import validate_link | ||
validate_link(source, self, link_type, link_label) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this needed? Not already in Node
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I remember now, this needs to be in the subclasses Data
, CalculationNode
and WorkflowNode
, because the tests currently, for simplicity, use base Nodes
to create dummy graphs. If I were to put these checks in the Node
base class, the type checks would fail. Until we rewrite the tests to only ever use Data
, CalculationNode
and WorkflowNode
instances to create dummy graphs, this has to stay in the sub classes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair one
aiida/work/processes.py
Outdated
# Need this special case for tests that use ProcessNodes as classes | ||
if isinstance(self.calc, ProcessNode) and not isinstance(self.calc, (CalculationNode, WorkflowNode)): | ||
self.calc.add_incoming(input_value, LinkType.INPUT_WORK, name) | ||
continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
continue |
58f8ed6
to
ff8ba5b
Compare
The link triple is defined as the tuple of the source node, link type and link label of an incoming link into a target node. This concept is implemented in a named tuple `LinkTriple`. Since the introduction of link types, the link label of incoming links no longer necessarily needs to be unique, as long as the triple with the link type and source node *is* unique. However, the old implementation of the incoming link cache relied on label uniqueness, because it used only the label as the key in the cache, which used a dictionary as its data structure. Here we change that data structure of the node's internal incoming link cache to be a set of link triples.
ddc18d0
to
1d94bad
Compare
The `link_label` used to be optional when specifying a link, as long as both nodes were already stored. If one of the nodes was unstored the link had to be stored in the internal cache which was indexed on the link label. This was already changed to be indexed on the link triple instead, solving that issue, but the auto label generation in `Node._add_dblink_from` in the case of no label being defined, relied on a uniqueness constraint of the link label on the database. This constraint had already been removed a long time ago when the `RETURN` link type was introduced. However, the logic of the auto label generation was kept, even though it would never work. We remove that functionality here and make the explicit passing of a link label required in the `add_incoming` and `_add_dblink_from` methods.
1d94bad
to
834e27a
Compare
Fixes #2177
With the new ORM hierarchy in place, the corresponding link types can be defined:
CREATE
:CalculationNode -> Data
RETURN
:WorkflowNode -> Data
INPUT_CALC
:Data -> CalculationNode
INPUT_WORK
:Data -> WorkflowNode
CALL_CALC
:WorkflowNode -> CalculationNode
CALL_WORK
:WorkflowNode -> WorkflowNode
These rules are encoded in the
validate_link
function inaiida.orm.utils.links
.This function is called from
add_link_from
, which has been renamed toadd_incoming
,and validates the triple (source, link, target) representing the addition of a link
from a source node to a target node.