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: LowerTypes pass allows replacing extension types and ops #1989

Open
wants to merge 41 commits into
base: main
Choose a base branch
from

Conversation

acl-cqc
Copy link
Contributor

@acl-cqc acl-cqc commented Mar 17, 2025

Also added HugrMut::optype_mut.

Some rough edges - naming? (e.g. LowerTypes, ChangeTypeError).

If you replace a Copyable type with a Linear one, you'll either get a SignatureError (if this results in a type like ArgMustBeCopyable[Qubit]) or an invalid Hugr. (Or you might get lucky, if every outgoing port of the type happens to have one successor!) Adding dup / free will follow in a later PR.

Deprecation of OpDef lower_funcs and enum LowerFunc, and the previous hugr_passes::lower, to follow in another PR.

Copy link

codecov bot commented Mar 17, 2025

Codecov Report

Attention: Patch coverage is 84.54936% with 72 lines in your changes missing coverage. Please review.

Project coverage is 83.71%. Comparing base (6ed4ecf) to head (d19dc5a).

Files with missing lines Patch % Lines
hugr-passes/src/lower_types.rs 84.76% 54 Missing and 15 partials ⚠️
hugr-core/src/hugr/internal.rs 66.66% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main    #1989    +/-   ##
========================================
  Coverage   83.71%   83.71%            
========================================
  Files         209      210     +1     
  Lines       39192    39655   +463     
  Branches    35863    36326   +463     
========================================
+ Hits        32808    33199   +391     
- Misses       4540     4596    +56     
- Partials     1844     1860    +16     
Flag Coverage Δ
python 92.03% <ø> (ø)
rust 82.95% <84.54%> (+0.01%) ⬆️

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 changed the base branch from main to acl/type_transform March 17, 2025 20:14
Base automatically changed from acl/type_transform to main March 19, 2025 12:04
impl LowerTypes {
/// Sets the validation level used before and after the pass is run.
// Note the self -> Self style is consistent with other passes, but not the other methods here.
// TODO change the others? But we are planning to drop validation_level in https://github.com/CQCL/hugr/pull/1895
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately this is likely to go in before #1895 as this is needed now but #1895 is a breaking change that will likely come in April

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Opinions welcome, if I'm gonna change the others I'll do it in this PR

/// original op modulo the required type substitutions. (If signatures are incorrect,
/// it is likely that the wires in the Hugr will be invalid, so this gives an early warning
/// by instead raising [ChangeTypeError::SignatureMismatch].)
pub fn check_signatures(&mut self, check_sig: bool) {
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'm not really sure about this checking stuff, hence no tests. I could drop it altogether, or convert to a panic (as it was before a0ac6d6). Thoughts?

if let Some(old_sig) = hugr.get_optype(n).dataflow_signature() {
let old_sig = old_sig.into_owned();
let mut expected_sig = old_sig.clone();
expected_sig.transform(self)?;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hence can't use self.check_sig.then_some()

let mut h = mb.finish_hugr().unwrap();

assert!(lower_types(&ext).run(&mut h).unwrap());
// We do not update the extension delta
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we need to update the delta, probably by rerunning extension "inference" to recompute union over children, we can do this, but would definitely prefer to leave for another PR unless it's urgent

pub fn lower_parametric_type(
&mut self,
src: &TypeDef,
dest_fn: Box<dyn Fn(&[TypeArg]) -> Type>, // TODO should we return Option<Type> ?
Copy link
Contributor Author

@acl-cqc acl-cqc Mar 19, 2025

Choose a reason for hiding this comment

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

Thoughts? Even returning Type doesn't actually force replacing all instances of the parametric type as the Fn could return the same type again (with the same or different typeargs), and returning Option allows better/easier "change checking" (None => return False for unchanged)

pub fn lower_parametric_op(
&mut self,
src: &OpDef,
dest_fn: Box<dyn Fn(&[TypeArg]) -> Option<OpReplacement>>, // TODO or just OpReplacement?
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'm leaning towards keeping the Option here

pub fn lower_consts(
&mut self,
src_ty: &CustomType,
const_fn: Box<dyn Fn(&OpaqueValue) -> Option<Value>>, // TODO should we return Value??
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Leaning towards keeping the Option. Oh, I should doc that None means "no change"

OpType::OpaqueOp(_) => panic!("OpaqueOp should not be in a Hugr"),

OpType::AliasDecl(_) | OpType::Module(_) => Ok(false),
_ => todo!(),
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 is the catch-all because OpType is #[non_exhaustive], I believe I've handled all current OpTypes and there is no more to do right now

@acl-cqc acl-cqc marked this pull request as ready for review March 19, 2025 14:42
@acl-cqc acl-cqc requested a review from a team as a code owner March 19, 2025 14:42
@acl-cqc acl-cqc requested a review from ss2165 March 19, 2025 14:42
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.

1 participant