Skip to content

Conversation

@acl-cqc
Copy link
Contributor

@acl-cqc acl-cqc commented Jul 15, 2025

The goal here is to improve compositionality - if you configure ReplaceTypes to convert type A to type B, and also type B to type C, then if it sees an A it "should" produce a C. (This is not the case at present, you'll get a B.) Likewise for op-replacements containing ops that themselves would be replaced - so this is a step towards addressing #2668 (via the second route therein).

I'd kinda like to turn this option on by default, but that would be a behaviour-breaking change (very breaking if you e.g. replaced bool by Either<bool, Future> - thankfully we replace tket::bool with ...bool_t so that one would be ok - or an op with a DFG containing that op, say; such cases would infinite-loop). There are a couple of possible ways of doing this:

  • deprecate the non-ReplacementOptions-taking variants, and add replace_(parametrized_)type_recursive (likely 4 methods) as the new "Options-less" methods (i.e. which we consider the "default" methods)...that name's not good tho
  • make the "Options-less" methods specify some ReplacementOptions::without_recursion() (where ReplacementOptions::default() includes recursion). But that's not really "on by default"....

A second consideration is whether we should have methods for consts that take ReplacementOptions too. This is not strictly necessary: unlike replacing types/ops, the callback for replacing consts is given the &ReplaceTypes (it has to, as the framework cannot see include CustomConsts, whereas it can for custom types or ops). So a const callback can call the ReplaceTypes again when it's done (if it's changed the outermost type constructor); but that seems a hard ask for callback authors to remember....

Thoughts on both?

@codecov
Copy link

codecov bot commented Jul 15, 2025

Codecov Report

❌ Patch coverage is 96.61017% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.48%. Comparing base (3d1ea85) to head (9a0bec2).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
hugr-passes/src/replace_types.rs 96.61% 3 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2442      +/-   ##
==========================================
+ Coverage   83.41%   83.48%   +0.06%     
==========================================
  Files         262      262              
  Lines       51176    51395     +219     
  Branches    46737    46956     +219     
==========================================
+ Hits        42689    42907     +218     
+ Misses       6109     6108       -1     
- Partials     2378     2380       +2     
Flag Coverage Δ
python 91.46% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@acl-cqc acl-cqc requested a review from mark-koch July 15, 2025 15:16
Base automatically changed from acl/replace_recursive to main July 16, 2025 12:45
@acl-cqc acl-cqc force-pushed the acl/replace_recursive2 branch from 58a05b1 to 1c9857f Compare November 5, 2025 15:51
}

/// Options for how the replacement for an op is processed.
// TODO also need to apply to replacement consts:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Or do we, see description

@acl-cqc acl-cqc marked this pull request as ready for review November 5, 2025 17:39
@acl-cqc acl-cqc requested a review from a team as a code owner November 5, 2025 17:39
@acl-cqc acl-cqc marked this pull request as draft November 6, 2025 08:37
for region_root in regions {
for n in hugr.descendants(*region_root).collect::<Vec<_>>() {
changed |= self.change_node(hugr, n)?;
if n != hugr.entrypoint() && changed {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note this is clearly wrong - it means linearization applies to the first node that changes and all nodes coming after that (!)

for n in hugr.descendants(root).collect::<Vec<_>>() {
if self.change_node(hugr, n)? {
changed = true;
} else if !linearize_unchanged_ops {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This should fix the "bug" in the other comment but tests are now breaking!

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.

2 participants