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

feat: add Subgraph::copy_in_parent #182

Merged
merged 26 commits into from
Feb 5, 2025
Merged

Conversation

acl-cqc
Copy link
Contributor

@acl-cqc acl-cqc commented Jan 22, 2025

closes #171.

As suggested there, using Subgraph to define the, erm, subgraph to copy seemed good.

  • Since Subgraph borrows its parent graph, that reference has to be the ref via which the parent is mutated, so this meant implementing LinkView and LinkMut for &mut G
  • And also dropping some redundant : Clone bounds, since &muts do not implement Clone

The API seems slightly strange in that the Subgraph defines what to copy, but of course then does not include the copy of that just made, but I think that makes sense.

Note that Subgraph never implements LinkMut so we avoid the complex case of the parent itself being a Subgraph :)

Copy link

codecov bot commented Jan 22, 2025

Codecov Report

Attention: Patch coverage is 84.42211% with 31 lines in your changes missing coverage. Please review.

Project coverage is 83.54%. Comparing base (97fa7b8) to head (a2bd930).
Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/view/refs.rs 31.81% 30 Missing ⚠️
src/view/subgraph.rs 99.35% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #182      +/-   ##
==========================================
+ Coverage   83.27%   83.54%   +0.27%     
==========================================
  Files          24       24              
  Lines        6117     6316     +199     
  Branches     6117     6316     +199     
==========================================
+ Hits         5094     5277     +183     
- Misses        947      967      +20     
+ Partials       76       72       -4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@acl-cqc acl-cqc changed the title feat: add Subgraph::copy_in_parent feat!: add Subgraph::copy_in_parent Jan 22, 2025
@acl-cqc acl-cqc changed the title feat!: add Subgraph::copy_in_parent feat: add Subgraph::copy_in_parent Jan 22, 2025
@acl-cqc acl-cqc requested a review from lmondada January 27, 2025 12:02
Copy link
Contributor

@lmondada lmondada left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, looks good to me, other than one small bug I think.

I'd rather get @aborgna-q's opinion on implementing the portgraph traits for references, however. I don't know all the consequences of adding the blanket trait impls. Are we fine with barring users from definining their own impls on references?

/// Returns a map from node indices within this subgraph, to the indices
/// of the newly-created nodes in the parent graph (they are not in this subgraph!);
/// or a LinkError in which case the underlying graph will not have had any nodes added
/// (however subports may have been for a [MultiView]).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be cleanest for the API if you would differentiate between the "expected" error of finding a boundary edge in a non-Multiview graph, vs the "seriously wrong error" of a link failing to get added for inscrutable reasons (and in which case it is hard to know whether other ports have already been added).

In the first case you could return a better error than a LinkError... But this might be unnecessarily complicating things.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's fair, it did make me think "ugh, hmmm" a bit while I was writing it. Lemme have a go...

Copy link
Contributor Author

@acl-cqc acl-cqc Feb 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, so the first thing I note is that any LinkError for internal edges must be a bug in copy_in_parent - perhaps we should unwrap that rather than return to the user. Then we can say any LinkError must be from the boundary edges. Moreover, the only possibility is AlreadyLinked if we are copying in a plain portgraph - we could panic/unwrap on any other variant as again that must be a bug in copy_in_parent. It would be nice thus to define an error type that for MultiPortgraphs is Infallible but for plain Portgraphs is only the "AlreadyLinked" variant of LinkError...

This may be too aggressive, it's not really the style of the rest of portgraph, @aborgna-q may have opinions here...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I would remove the subports-added on failure note. Those are implementation details and not visible to the user unless they peel the multiport layer off.

  • Unwrap (or rather expect) on internal bugs seems reasonable. Errors should be for user bugs.

  • Use the standard rustdoc headers to clean this up a bit

    /// # Returns
    ///
    ///   A map from node indices within this subgraph, to the indices
    ///   of the newly-created nodes in the parent graph (they are not in this subgraph!).
    ///
    /// # Errors
    ///
    /// - [CopySubgraphError::CantCopyBoundary] if the boundary was not empty, but graph doesn't support support multilinks. The underlying graph remains unchanged.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good plan, thanks - and that lead to spotting that I hadn't re-exported CopySubgraphError :). I kept a note that capacity may have changed rather than subports.

@lmondada lmondada requested a review from aborgna-q January 27, 2025 15:39
@acl-cqc
Copy link
Contributor Author

acl-cqc commented Feb 3, 2025

@aborgna-q given Luca has requested your review, shall I skip re-requesting Luca's ?

Copy link
Collaborator

@aborgna-q aborgna-q left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

A cherry on top would be to add a benchmark using the new benchmarking framework (see convex for an example). But feel free to ignore :)

@@ -63,4 +107,32 @@ impl<G: MultiView> MultiView for &G {
}
}

// TODO: PortMut, LinkMut, MultiMut
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add MultiMut too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, also MultiView for &mut G

Comment on lines 325 to 328
impl<G: LinkMut> Subgraph<G>
where
G::LinkEndpoint: Into<PortIndex>,
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already a bound on LinkEndpoint

type LinkEndpoint: Into<PortIndex> + Copy;

Suggested change
impl<G: LinkMut> Subgraph<G>
where
G::LinkEndpoint: Into<PortIndex>,
{
impl<G: LinkMut> Subgraph<G> {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I thought I was so clever for thinking of that!! :-D

/// Copies all the nodes and edges in this subgraph into the parent graph.
/// If there are any boundary edges, these will also be copied but keeping
/// the same *external* end port (this will fail unless the underlying graph
/// is a [MultiView]).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// is a [MultiView]).
/// is a [`MultiMut`]).

where
G::LinkEndpoint: Into<PortIndex>,
{
/// Copies all the nodes and edges in this subgraph into the parent graph.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first line is used as a short description on module listings.

Suggested change
/// Copies all the nodes and edges in this subgraph into the parent graph.
/// Copies all the nodes and edges in this subgraph into the parent graph.
///

/// Returns a map from node indices within this subgraph, to the indices
/// of the newly-created nodes in the parent graph (they are not in this subgraph!);
/// or a LinkError in which case the underlying graph will not have had any nodes added
/// (however subports may have been for a [MultiView]).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I would remove the subports-added on failure note. Those are implementation details and not visible to the user unless they peel the multiport layer off.

  • Unwrap (or rather expect) on internal bugs seems reasonable. Errors should be for user bugs.

  • Use the standard rustdoc headers to clean this up a bit

    /// # Returns
    ///
    ///   A map from node indices within this subgraph, to the indices
    ///   of the newly-created nodes in the parent graph (they are not in this subgraph!).
    ///
    /// # Errors
    ///
    /// - [CopySubgraphError::CantCopyBoundary] if the boundary was not empty, but graph doesn't support support multilinks. The underlying graph remains unchanged.

@hugrbot
Copy link
Collaborator

hugrbot commented Feb 5, 2025

🐰 Bencher Report

Branchacl/subgraph_copy_in_parent
Testbedubuntu-latest
Click to view all benchmark results
BenchmarkEstimated CyclesBenchmark Result
cycles
(Result Δ%)
Lower Boundary
cycles
(Limit %)
Upper Boundary
cycles
(Limit %)
InstructionsBenchmark Result
1e3 x instructions
(Result Δ%)
Upper Boundary
1e3 x instructions
(Limit %)
L1 HitsBenchmark Result
1e3 x hits
(Result Δ%)
Upper Boundary
1e3 x hits
(Limit %)
L2 HitsBenchmark Result
hits
(Result Δ%)
Upper Boundary
hits
(Limit %)
RAM HitsBenchmark Result
hits
(Result Δ%)
Upper Boundary
hits
(Limit %)
Total read+writeBenchmark Result
1e3 x reads/writes
(Result Δ%)
Upper Boundary
1e3 x reads/writes
(Limit %)
iai_benches::convex::callgrind_convex_construction big:ConvexConstruction :: big()📈 view plot
🚷 view threshold
35,960,839.00
(+0.00%)
35,951,259.73
(99.97%)
35,960,839.00
(100.00%)
📈 view plot
🚷 view threshold
25,493.12📈 view plot
🚷 view threshold
35,647.39📈 view plot
🚷 view threshold
25,519.00
(-0.00%)
25,521.00
(99.99%)
📈 view plot
🚷 view threshold
5,310.00
(+0.02%)
5,310.00
(100.00%)
📈 view plot
🚷 view threshold
35,678.22
iai_benches::convex::callgrind_convex_construction small:ConvexConstruction :: small()📈 view plot
🚷 view threshold
367,674.00
(+0.01%)
358,094.73
(97.39%)
367,674.00
(100.00%)
📈 view plot
🚷 view threshold
258.69📈 view plot
🚷 view threshold
360.35📈 view plot
🚷 view threshold
15.00
(-6.25%)
17.00
(88.24%)
📈 view plot
🚷 view threshold
207.00
(+0.49%)
207.00
(100.00%)
📈 view plot
🚷 view threshold
360.58
iai_benches::convex::callgrind_convex_full big:ConvexFull :: big()📈 view plot
🚷 view threshold
20,322,742.00
(-0.00%)
20,309,097.72
(99.93%)
20,322,828.00
(100.00%)
📈 view plot
🚷 view threshold
15,776.01📈 view plot
🚷 view threshold
19,772.57
(-0.00%)
19,772.57
(100.00%)
📈 view plot
🚷 view threshold
69,491.00
(+0.00%)
69,491.00
(100.00%)
📈 view plot
🚷 view threshold
5,792.00
(-0.03%)
5,795.00
(99.95%)
📈 view plot
🚷 view threshold
19,847.85
iai_benches::convex::callgrind_convex_full small:ConvexFull :: small()📈 view plot
🚷 view threshold
138,635.00
(-0.04%)
119,596.47
(86.27%)
138,755.00
(99.91%)
📈 view plot
🚷 view threshold
106.24📈 view plot
🚷 view threshold
133.29📈 view plot
🚷 view threshold
54.00
(+3.85%)
54.00
(100.00%)
📈 view plot
🚷 view threshold
145.00
(-1.36%)
149.00
(97.32%)
📈 view plot
🚷 view threshold
133.49
iai_benches::convex::callgrind_convex_sparse big:ConvexSparse :: big()📈 view plot
🚷 view threshold
93,161.00
(-0.07%)
72,853.23
(78.20%)
93,289.00
(99.86%)
📈 view plot
🚷 view threshold
62.58📈 view plot
🚷 view threshold
84.64
(+0.00%)
84.64
(100.00%)
📈 view plot
🚷 view threshold
326.00
(+0.31%)
326.00
(100.00%)
📈 view plot
🚷 view threshold
197.00
(-1.01%)
201.00
(98.01%)
📈 view plot
🚷 view threshold
85.16
iai_benches::convex::callgrind_convex_sparse small:ConvexSparse :: small()📈 view plot
🚷 view threshold
12,042.00
(-0.53%)
-8,265.77
(-68.64%)
12,170.00
(98.95%)
📈 view plot
🚷 view threshold
6.11📈 view plot
🚷 view threshold
8.34
(+0.01%)
8.34
(100.00%)
📈 view plot
🚷 view threshold
6.00
(+20.00%)
6.00
(100.00%)
📈 view plot
🚷 view threshold
105.00
(-1.87%)
109.00
(96.33%)
📈 view plot
🚷 view threshold
8.45
iai_benches::hierarchy::callgrind_create_hierarchy big:CreateHierarchy :: big()📈 view plot
🚷 view threshold
3,870,450.00
(+0.00%)
3,865,021.75
(99.86%)
3,870,450.00
(100.00%)
📈 view plot
🚷 view threshold
2,470.62📈 view plot
🚷 view threshold
3,589.92
(-0.00%)
3,589.92
(100.00%)
📈 view plot
🚷 view threshold
3,410.00📈 view plot
🚷 view threshold
7,528.00
(+0.01%)
7,528.00
(100.00%)
📈 view plot
🚷 view threshold
3,600.86
iai_benches::hierarchy::callgrind_create_hierarchy small:CreateHierarchy :: small()📈 view plot
🚷 view threshold
41,357.00
(+0.04%)
35,928.75
(86.87%)
41,357.00
(100.00%)
📈 view plot
🚷 view threshold
25.68📈 view plot
🚷 view threshold
37.28
(-0.00%)
37.28
(100.00%)
📈 view plot
🚷 view threshold
3.00📈 view plot
🚷 view threshold
116.00
(+0.43%)
116.00
(100.00%)
📈 view plot
🚷 view threshold
37.40
iai_benches::hierarchy::callgrind_traverse_hierarchy big:TraverseHierarchy :: big()📈 view plot
🚷 view threshold
1,932,532.00
(-0.00%)
1,931,262.76
(99.93%)
1,932,540.00
(100.00%)
📈 view plot
🚷 view threshold
1,311.11📈 view plot
🚷 view threshold
1,893.94
(+0.00%)
1,893.94
(100.00%)
📈 view plot
🚷 view threshold
7,579.00
(-0.01%)
7,581.00
(99.97%)
📈 view plot
🚷 view threshold
20.00📈 view plot
🚷 view threshold
1,901.54
iai_benches::hierarchy::callgrind_traverse_hierarchy small:TraverseHierarchy :: small()📈 view plot
🚷 view threshold
22,008.00📈 view plot
🚷 view threshold
14.31📈 view plot
🚷 view threshold
20.63📈 view plot
🚷 view threshold
10.00📈 view plot
🚷 view threshold
38.00📈 view plot
🚷 view threshold
20.68
iai_benches::portgraph::callgrind_clone_portgraph big:ClonePortgraph :: big()📈 view plot
🚷 view threshold
54,346,918.00
(-0.00%)
54,341,523.75
(99.99%)
54,346,952.00
(100.00%)
📈 view plot
🚷 view threshold
4,841.33📈 view plot
🚷 view threshold
6,216.84
(+0.00%)
6,216.84
(100.00%)
📈 view plot
🚷 view threshold
0.00📈 view plot
🚷 view threshold
1,375,145.00
(-0.00%)
1,375,146.00
(100.00%)
📈 view plot
🚷 view threshold
7,591.99
iai_benches::portgraph::callgrind_clone_portgraph small:ClonePortgraph :: small()📈 view plot
🚷 view threshold
7,006.00📈 view plot
🚷 view threshold
2.41📈 view plot
🚷 view threshold
3.40📈 view plot
🚷 view threshold
0.00📈 view plot
🚷 view threshold
103.00📈 view plot
🚷 view threshold
3.50
iai_benches::portgraph::callgrind_make_portgraph big:MakePortgraph :: big()📈 view plot
🚷 view threshold
709,381,125.00
(-0.00%)
709,370,336.50
(100.00%)
709,381,193.00
(100.00%)
📈 view plot
🚷 view threshold
525,001.64📈 view plot
🚷 view threshold
685,314.65
(+0.00%)
685,314.65
(100.00%)
📈 view plot
🚷 view threshold
4.00📈 view plot
🚷 view threshold
687,613.00
(-0.00%)
687,615.00
(100.00%)
📈 view plot
🚷 view threshold
686,002.27
iai_benches::portgraph::callgrind_make_portgraph small:MakePortgraph :: small()📈 view plot
🚷 view threshold
76,572.00
(-0.04%)
65,783.50
(85.91%)
76,640.00
(99.91%)
📈 view plot
🚷 view threshold
54.31📈 view plot
🚷 view threshold
70.97
(+0.00%)
70.97
(100.00%)
📈 view plot
🚷 view threshold
8.00📈 view plot
🚷 view threshold
159.00
(-0.62%)
161.00
(98.76%)
📈 view plot
🚷 view threshold
71.13
iai_benches::portgraph::callgrind_remove_unordered big:RemoveUnordered :: big()📈 view plot
🚷 view threshold
2,804,026.00
(+0.00%)
2,787,741.25
(99.42%)
2,804,026.00
(100.00%)
📈 view plot
🚷 view threshold
2,068.41📈 view plot
🚷 view threshold
2,560.65
(-0.00%)
2,560.65
(100.00%)
📈 view plot
🚷 view threshold
47,541.00📈 view plot
🚷 view threshold
162.00
(+0.93%)
162.00
(100.00%)
📈 view plot
🚷 view threshold
2,608.35
iai_benches::portgraph::callgrind_remove_unordered small:RemoveUnordered :: small()📈 view plot
🚷 view threshold
26,966.00
(+0.18%)
11,319.87
(41.98%)
26,966.00
(100.00%)
📈 view plot
🚷 view threshold
18.94📈 view plot
🚷 view threshold
25.06
(-0.00%)
25.06
(99.99%)
📈 view plot
🚷 view threshold
10.00
(-4.76%)
11.00
(90.91%)
📈 view plot
🚷 view threshold
53.00
(+2.91%)
53.00
(100.00%)
📈 view plot
🚷 view threshold
25.12
iai_benches::portgraph::callgrind_resize_ports big:ResizePorts :: big()📈 view plot
🚷 view threshold
9,306,675.00
(+0.00%)
9,302,523.99
(99.96%)
9,306,675.00
(100.00%)
📈 view plot
🚷 view threshold
6,546.11📈 view plot
🚷 view threshold
9,028.62
(+0.00%)
9,028.62
(100.00%)
📈 view plot
🚷 view threshold
13,708.00
(-0.01%)
13,710.00
(99.99%)
📈 view plot
🚷 view threshold
5,986.00
(+0.01%)
5,986.00
(100.00%)
📈 view plot
🚷 view threshold
9,048.32
iai_benches::portgraph::callgrind_resize_ports small:ResizePorts :: small()📈 view plot
🚷 view threshold
98,309.00
(+0.02%)
92,880.75
(94.48%)
98,309.00
(100.00%)
📈 view plot
🚷 view threshold
67.21📈 view plot
🚷 view threshold
89.85
(-0.00%)
89.86
(100.00%)
📈 view plot
🚷 view threshold
18.00📈 view plot
🚷 view threshold
239.00
(+0.21%)
239.00
(100.00%)
📈 view plot
🚷 view threshold
90.11
iai_benches::render::callgrind_render_dot big:RenderDot :: big()📈 view plot
🚷 view threshold
613,682,342.00
(-0.00%)
613,183,215.20
(99.92%)
613,685,488.00
(100.00%)
📈 view plot
🚷 view threshold
421,056.93📈 view plot
🚷 view threshold
608,262.81
(+0.00%)
608,262.81
(100.00%)
📈 view plot
🚷 view threshold
37,378.00
(-0.69%)
37,901.00
(98.62%)
📈 view plot
🚷 view threshold
149,504.00
(-0.01%)
149,535.00
(99.98%)
📈 view plot
🚷 view threshold
608,449.69
iai_benches::render::callgrind_render_dot small:RenderDot :: small()📈 view plot
🚷 view threshold
6,159,284.00
(+0.00%)
6,157,368.15
(99.97%)
6,159,284.00
(100.00%)
📈 view plot
🚷 view threshold
4,085.79📈 view plot
🚷 view threshold
6,034.53
(-0.00%)
6,034.55
(100.00%)
📈 view plot
🚷 view threshold
2,074.00
(+0.48%)
2,074.00
(100.00%)
📈 view plot
🚷 view threshold
3,268.00
(-0.03%)
3,270.00
(99.94%)
📈 view plot
🚷 view threshold
6,039.88
iai_benches::render::callgrind_render_mermaid big:RenderMermaid :: big()📈 view plot
🚷 view threshold
192,073,667.00
(+0.06%)
152,943,486.36
(79.63%)
192,073,667.00
(100.00%)
📈 view plot
🚷 view threshold
134,005.82
(+0.00%)
134,011.89
(100.00%)
📈 view plot
🚷 view threshold
187,783.13
(-0.02%)
187,843.94
(99.97%)
📈 view plot
🚷 view threshold
241,919.00
(+14.48%)
241,919.00
(100.00%)
📈 view plot
🚷 view threshold
88,027.00
(-0.00%)
88,031.00
(100.00%)
📈 view plot
🚷 view threshold
188,113.07
(+0.00%)
188,113.07
(100.00%)
iai_benches::render::callgrind_render_mermaid small:RenderMermaid :: small()📈 view plot
🚷 view threshold
1,910,044.00
(+0.05%)
1,626,497.75
(85.15%)
1,910,044.00
(100.00%)
📈 view plot
🚷 view threshold
1,306.73
(-0.00%)
1,307.11
(99.97%)
📈 view plot
🚷 view threshold
1,858.48
(-0.01%)
1,858.97
(99.97%)
📈 view plot
🚷 view threshold
1,451.00
(+19.23%)
1,451.00
(100.00%)
📈 view plot
🚷 view threshold
1,266.00
(-0.08%)
1,268.00
(99.84%)
📈 view plot
🚷 view threshold
1,861.20
(-0.00%)
1,861.22
(100.00%)
iai_benches::toposort::callgrind_toposort small:Toposort :: small()📈 view plot
🚷 view threshold
312,739.00
(+0.01%)
303,798.35
(97.14%)
312,739.00
(100.00%)
📈 view plot
🚷 view threshold
220.69📈 view plot
🚷 view threshold
308.61
(+0.00%)
308.61
(100.00%)
📈 view plot
🚷 view threshold
13.00
(-10.34%)
16.00
(81.25%)
📈 view plot
🚷 view threshold
116.00
(+0.87%)
116.00
(100.00%)
📈 view plot
🚷 view threshold
308.74
🐰 View full continuous benchmarking report in Bencher

@acl-cqc
Copy link
Contributor Author

acl-cqc commented Feb 5, 2025

I've raised #186 for benchmarking as I'd be interested to do it but think this is blocking too many things ATM.

@acl-cqc acl-cqc added this pull request to the merge queue Feb 5, 2025
Merged via the queue into main with commit aebb42e Feb 5, 2025
10 checks passed
@acl-cqc acl-cqc deleted the acl/subgraph_copy_in_parent branch February 5, 2025 10:51
@hugrbot hugrbot mentioned this pull request Feb 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add LinkMut::copy_subgraph
4 participants