-
Notifications
You must be signed in to change notification settings - Fork 13
feat: add trait+funcs for linking Hugrs explicitly by Node (linking 2/4) #2521
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
base: main
Are you sure you want to change the base?
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2521 +/- ##
==========================================
+ Coverage 82.75% 82.93% +0.17%
==========================================
Files 252 253 +1
Lines 47065 47530 +465
Branches 42581 43046 +465
==========================================
+ Hits 38950 39417 +467
+ Misses 6055 6050 -5
- Partials 2060 2063 +3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
3a47eb3
to
5774665
Compare
|
transfers: Transfers<SN, TGT::Node>, | ||
node_map: &mut HashMap<SN, TGT::Node>, | ||
) { | ||
// Resolve `use_existing` first in case the existing node is also replaced by |
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.
No test yet, coming up...(EDIT: added)
Alternatively we could error if the same existing-node is target of both a replace
and a UseExisting
.
hugr-core/src/builder.rs
Outdated
@@ -352,4 +363,33 @@ pub(crate) mod test { | |||
); | |||
hugr | |||
} | |||
|
|||
/// A DFG-entrypoint Hugr (no inputs, one bool_t output) containing two calls, | |||
/// to a FuncDefn and a FuncDecl each bool_t->bool_t, and their handles. |
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.
...and their handles
I think some words are missing. You maybe meant "Returns the hugr and the function handles"?
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.
oh i see you just copied this over. Might be worth fixing anyways :)
let node = self | ||
.hugr_mut() | ||
.insert_hugr_link_nodes(parent, hugr, defns)?[&ep]; | ||
wire_ins_return_outs(input_wires, node, self) | ||
} | ||
|
||
/// Copy a hugr-defined op into the sibling graph, wiring up the |
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 find this docstring confusing, what "hugr-defined op" does this refer to? The entrypoint I suppose:
/// Copy a hugr-defined op into the sibling graph, wiring up the | |
/// Copy the op designed by the HUGR entrypoint into the sibling graph, wiring up the |
hugr-core/src/hugr/hugrmut.rs
Outdated
@@ -833,39 +840,7 @@ mod test { | |||
.chain(call2.then_some((decl.node(), h.module_root())).into_iter()), | |||
); | |||
h.insert_forest(insert, roots).unwrap(); |
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.
this is in your previous PR I think, but anyways:
in the docs of fn insert_forest
, can you change any reference to root_parents
to roots
, to match the argument name?
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 was going to make another comment about using rstest instead of the for
loop, but I see that this seems already taken care of in #2518.
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.
Yes, sorry, I'm rather behind on merging, hoping 2518 goes in soon!
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.
Docs for insert_forest
updated (now on main
), should be showing up here
hugr-core/src/hugr/linking.rs
Outdated
hugr::{HugrMut, internal::HugrMutInternals}, | ||
}; | ||
|
||
/// Methods for linking Hugrs, i.e. merging the Hugrs and adding edges between old and inserted nodes. |
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.
A bit shorter so it fits in 80chars
/// Methods for linking Hugrs, i.e. merging the Hugrs and adding edges between old and inserted nodes. | |
/// Methods that merge Hugrs, adding edges between old and inserted nodes. |
hugr-core/src/hugr/linking.rs
Outdated
/// | ||
/// If `parent` is `Some` but not in the graph. | ||
#[allow(clippy::type_complexity)] | ||
fn insert_from_view_link_nodes<H: HugrView>( |
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.
These names make sense, but I wonder if we could shorten them a bit -- or do you plan on renaming to insert_hugr
and insert_from_view
once those are deleted?
I can suggest:
insert_link_from_view
insert_link_hugr
I think they are just as clear and sound like natural "evolutions" of the current API. Up to you :)
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.
Later I'm going to add variants that do linking by name, using FuncDefn::func_name
and FuncDecl::func_name
of Visibility::Public
functions; so I'd like either
- method names that say
...node...
here and I can replacenode
byname
- method names that I can abbreviate (e.g. by removing
node
altogether), as linking by name is likely to be the common/usual case. (This is the hardcode explicit option if you want more control; and linking by name reduces to this; so it is possible there won't be many calls of these methods.)
Happy to abbreviate insert_from_view
to insert_view
....I suspect a second trait (trait NodeLinking
and trait NameLinking
, heh) is probably too many, insert_nodelinked_hugr
/view
not much better. Hmmm. Will have a think.
hugr-core/src/hugr/linking.rs
Outdated
/// This is done by module-children from the inserted (source) Hugr replacing, or being replaced by, | ||
/// module-children already in the target Hugr; static edges from the replaced node, | ||
/// are transferred to come from the replacing node, and the replaced node(/subtree) then deleted. | ||
pub trait LinkHugr: HugrMut { |
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'm fine with leaving insert_hugr
and insert_from_view
as they are for the moment, but could you add a note in their docs pointing users here if they are considering using them?
E.g. say something like "more specialised methods that handle copying multiple module root children and preserving edge connectivity can be found in [LinkHugr
].
/// | ||
/// This is done by module-children from the inserted (source) Hugr replacing, or being replaced by, | ||
/// module-children already in the target Hugr; static edges from the replaced node, | ||
/// are transferred to come from the replacing node, and the replaced node(/subtree) then deleted. |
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.
Have you thought of other names for this trait? What I dislike about LinkHugr
is that it sounds like LinkView
in portgraph, which is read-only; a lot of our traits for mutation have a Mut
suffix, but I cannot think of a good name that would keep this convention.
InsertHugr
or HugrInsert
are pretty straight-forward, though? Maybe InsertHugrMut
if you want to highlight that this is a "specialisation" of HugrMut
...
Also, is there a reason this cannot be part of the HugrMut
trait? Or at least provide a blanket implementation for any H: HugrMut
?
I think there is a tradeoff between avoiding huge bloated traits and on the other hand a proliferation of traits... I find more traits make API exploration much harder as the available methods might get missed by Intellisense. A pointer from the HugrMut
docs to here would already help with that (see my other comment).
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.
Thanks, you make many good points here. I had not thought of LinkView
but that's a good reason not to call this LinkHugr
! Hmmm. Maybe trait HugrLinking
? With a Mut suffix....hmmm....maybe InsertHugrMut
is not bad, although next I'm planning to add link_module
(_view
) - and then finally insert_
variants that link by name, so those would be fine.
Separated out from HugrMut as per suggestion from @aborgna-q because that trait is getting pretty big and this is very much non-core functionality, and built entirely on top of HugrMut. I do see what you mean about Intellisense tho, hmmm.
But, there is a blanket implementation! :)
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.
Excellent, I think HugrLinking
is very good.
check_calls_defn_decl(&h, false, false); | ||
|
||
// Specify which decls to transfer. No real "linking" here though. | ||
for (call1, call2) in [(false, false), (false, true), (true, false), (true, 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.
here you could use rstest #[case] or #[values] as in the test_insert_forest
case :)
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.
The design looks solid, I like it! My main discussion points are... naming, as usual :)
Merged, and hope I've fixed all the thumbs-up'd bits, either here or in #2518 ! I've renamed to Also renamed
|
Ok, #2518 now merged into main, and I hope that's fixed a number of issues here. As mentioned, I've gone with |
@@ -220,17 +221,46 @@ impl<T: AsMut<Hugr> + AsRef<Hugr>> ModuleBuilder<T> { | |||
|
|||
Ok(AliasID::new(node, name, bound)) | |||
} | |||
|
|||
/// Adds some module-children of another Hugr to this module, with |
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.
/// Adds some module-children of another Hugr to this module, with | |
/// Add some module-children of another Hugr to this module, with |
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.
slightly subjective, but I think we mostly use the imperative form.
self.hugr_mut().add_hugr_link_nodes(None, other, children) | ||
} | ||
|
||
/// Copies module-children from a HugrView to this module, with |
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.
/// Copies module-children from a HugrView to this module, with | |
/// Copy module-children from a HugrView to this module, with |
/// | ||
/// This is done by module-children from the inserted (source) Hugr replacing, or being replaced by, | ||
/// module-children already in the target Hugr; static edges from the replaced node, | ||
/// are transferred to come from the replacing node, and the replaced node(/subtree) then deleted. |
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.
Excellent, I think HugrLinking
is very good.
hugr-core/src/hugr/linking.rs
Outdated
@@ -33,12 +33,12 @@ pub trait LinkHugr: HugrMut { | |||
/// | |||
/// If `parent` is `Some` but not in the graph. | |||
#[allow(clippy::type_complexity)] | |||
fn insert_from_view_link_nodes<H: HugrView>( | |||
fn add_view_link_nodes<H: HugrView>( |
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.
Ok, good name proposals!
-
I prefer
insert
toadd
(add
sounds to me like adding an element to a container, whereasinsert
is a more complex process?) I admit this is subjective, so up to you. -
Given your explanation, I think a good suffix for the names would be
_by_nodes
. Names withby
are pretty idiomatic. -
If you say linking by name will be the most common use case, then I am not fussed if the method names for linking explicitly by node are quite verbose. As you suggest I would just drop the
by_nodes
suffix for the link-by-name versions.
So maybe insert_link_view_by_nodes
and insert_link_hugr_by_nodes
? I don't know at this point 😅
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.
Ok, so
- I think you're right about
insert
rather thanadd
: I think "insert" is "put the entrypoint-subtree under a new parent" whereas "link" is "do stuff with the module children" (only). Using "add" for "maybe insert" is a faux pas, will just go with insert. - Let's also establish the "link....by_nodes" convention (foreshadowing upcoming+shorter "link...." that's by name), replacing current "...._link_nodes".
- I also think rejecting names for being too long may be a bad move here. There's an old idea that the more a method does, the longer its name should be. These methods do quite a lot, so long names are at least ok....
- Hence: I propose:
HugrMut::insert_link_hugr_by_node
(and s/hugr/view/), Dataflow builderadd_link_hugr_by_node
(where add in the builder means insert elsewhere but nvm), Module builderlink_hugr_by_node
(without the add: it only does stuff with module children).
Will enact shortly unless you object @lmondada...
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.
oh, by_node singular or plural? I think I favour singular
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.
Yes I like it!
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.
Ok, done. insert_link_view_....
isn't great because there is also a thing called a LinkView but it is portgraph really.
And the Dataflow builder now has add_link_hugr_by_node_with_wires
which is a lot of suffixes 👎 so better ideas welcome, but add.....with_wires_by_node
is worse (it's not the wires that are by node it's the "link"!)
Closes #2355.
This adds a new
trait HugrLinking: HugrMut
with two methods that allow inserting a Hugr/View intoself
- the entrypoint subtree (anywhere) and/or the module-children (under the module-root ofself
). An error is raised if you're trying to insert the entrypoint-subtree into two places. The added nodes can be linked i.e. static edges added from module-children to uses, both from new to existing or vice versa. The new methods are default-implemented on top ofHugrMut::insert_
(view_
)forest
, and there's a blanket impl for anyHugrMut
, so this is just to keep them separated out.Note: it would be possible to deprecate
insert_hugr
now:insert_link_nodes(other, Some(parent), HashMap::new())
does the same job (explicitly not copying any module-children), and returns an error, which is probably what we want. However, we'll be able to do a much better (more powerful) version in a couple of PRs time, so have left this for now.Largely a respin of #2285 (which was approved), keeping a similar API, but without the behaviour changes to
insert_hugr
andinsert_from_view
of that PR, and reimplemented. However, this adds the ability for a new node (e.g. FuncDefn) to replace existing nodes (e.g. aliased FuncDecls), and the new stuff is moved out into the new trait.