Skip to content

refactor!: More flexible pytket encoding #849

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

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

Conversation

aborgna-q
Copy link
Collaborator

@aborgna-q aborgna-q commented Apr 14, 2025

This PR rewrites the pytket encoding framework for hugr programs, and serves as a base for PRs to come.

Main changes from the existing implementation:

  • Extensibility:
    Extensions can define their own type, operation, and constant translations and register them at runtime.
    This is done by registering impl PytketEmitter objects, that lets us emit arbitrary pytket commands for a hugr operation.
    See tket2/src/serialize/pytket/extension/ for how it's done.
  • Support for arbitrary HugrViews with generic Node types (not just Hugr)
    • This includes sub-circuit support, although it's not exposed yet in the API.
  • HUGR wires can now be associated with a list of values
    • Support custom (extensible) types: Arrays, nested tuples, ...
    • Extension declare how many qubits, bits, and params are contained in their custom types.
  • Translate unsupported operations into black box barriers, grouping them as much as possible into convex subgraphs.

Circuits that could already be encoded produce the same pytket circuit (up to register reorderings) after this refactor.
Previously unsupported circuits will now be encoded using black box barriers, but we do not include decoding support here.

Not in this PR:

  • Support nested dfgs and (indirect) function calls. This will probably go in a followup PR.
  • Order edges between ops should generate barriers
  • Extracting the blackbox hugrs encoded in a pytket circuit's and merging back the chunks.
  • Tests of black-box cases. This is waiting until we can test the roundtrip.
  • Documentation an examples on how to define new custom emitters.
  • Define an encoder for -hseries ops.

BREAKING CHANGE: Removed some unused variants from TK1ConvertError and OpConvertError.

@hugrbot
Copy link
Collaborator

hugrbot commented Apr 14, 2025

This PR contains breaking changes to the public Rust API.

cargo-semver-checks summary

--- failure enum_missing: pub enum removed or renamed ---

Description:
A publicly-visible enum cannot be imported by its prior path. A `pub use` may have been removed, or the enum itself may have been renamed or removed entirely.
      ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
     impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.41.0/src/lints/enum_missing.ron

Failed in:
enum tket2::serialize::pytket::TK1ConvertError, previously in file /home/runner/work/tket2/tket2/BASELINE_BRANCH/tket2/src/serialize/pytket.rs:264

--- failure enum_tuple_variant_changed_kind: An enum tuple variant changed kind ---

Description:
A public enum's exhaustive tuple variant has changed to a different kind of enum variant, breaking possible instantiations and patterns.
      ref: https://doc.rust-lang.org/reference/items/enumerations.html
     impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.41.0/src/lints/enum_tuple_variant_changed_kind.ron

Failed in:
variant OpConvertError::UnsupportedOpSerialization in /home/runner/work/tket2/tket2/PR_BRANCH/tket2/src/serialize/pytket.rs:182

--- failure enum_variant_missing: pub enum variant removed or renamed ---

Description:
A publicly-visible enum has at least one variant that is no longer available under its prior name. It may have been renamed or removed entirely.
      ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
     impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.41.0/src/lints/enum_variant_missing.ron

Failed in:
variant OpConvertError::UnsupportedSerializedOp, previously in file /home/runner/work/tket2/tket2/BASELINE_BRANCH/tket2/src/serialize/pytket.rs:167
variant OpConvertError::UnsupportedInputType, previously in file /home/runner/work/tket2/tket2/BASELINE_BRANCH/tket2/src/serialize/pytket.rs:177
variant OpConvertError::UnsupportedOutputType, previously in file /home/runner/work/tket2/tket2/BASELINE_BRANCH/tket2/src/serialize/pytket.rs:190
variant OpConvertError::UnresolvedParamInput, previously in file /home/runner/work/tket2/tket2/BASELINE_BRANCH/tket2/src/serialize/pytket.rs:203
variant OpConvertError::TooManyOutputQubits, previously in file /home/runner/work/tket2/tket2/BASELINE_BRANCH/tket2/src/serialize/pytket.rs:214
variant OpConvertError::InvalidOpaqueTypeParam, previously in file /home/runner/work/tket2/tket2/BASELINE_BRANCH/tket2/src/serialize/pytket.rs:225

--- failure trait_method_added: pub trait method added ---

Description:
A non-sealed public trait added a new method without a default implementation, which breaks downstream implementations of the trait
      ref: https://doc.rust-lang.org/cargo/reference/semver.html#trait-new-item-no-default
     impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.41.0/src/lints/trait_method_added.ron

Failed in:
trait method tket2::serialize::pytket::TKETDecode::encode_with_config in file /home/runner/work/tket2/tket2/PR_BRANCH/tket2/src/serialize/pytket.rs:72
trait method tket2::serialize::TKETDecode::encode_with_config in file /home/runner/work/tket2/tket2/PR_BRANCH/tket2/src/serialize/pytket.rs:72

@aborgna-q aborgna-q changed the title refactor: More flexible and capable pytket encoding refactor: More flexible pytket encoding Apr 15, 2025
@aborgna-q aborgna-q force-pushed the ab/extract-pytket-extended branch from a260ac4 to f4ac585 Compare May 21, 2025 16:02
Copy link

codecov bot commented May 21, 2025

Codecov Report

Attention: Patch coverage is 66.80637% with 396 lines in your changes missing coverage. Please review.

Project coverage is 80.70%. Comparing base (5ae0ab9) to head (6607ea1).
Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
tket2/src/serialize/pytket/encoder.rs 63.42% 122 Missing and 17 partials ⚠️
...ket2/src/serialize/pytket/encoder/value_tracker.rs 81.62% 42 Missing and 10 partials ⚠️
tket2/src/serialize/pytket/encoder/config.rs 69.86% 40 Missing and 4 partials ⚠️
tket2/src/serialize/pytket/extension/prelude.rs 20.40% 38 Missing and 1 partial ⚠️
...rc/serialize/pytket/encoder/unsupported_tracker.rs 19.14% 38 Missing ⚠️
tket2/src/serialize/pytket/extension.rs 0.00% 25 Missing ⚠️
tket2/src/serialize/pytket/extension/tk2.rs 73.01% 14 Missing and 3 partials ⚠️
tket2/src/serialize/pytket/extension/float.rs 74.19% 15 Missing and 1 partial ⚠️
tket2/src/serialize/pytket.rs 52.38% 7 Missing and 3 partials ⚠️
tket2/src/serialize/pytket/extension/rotation.rs 85.71% 5 Missing and 2 partials ⚠️
... and 3 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #849      +/-   ##
==========================================
- Coverage   82.54%   80.70%   -1.84%     
==========================================
  Files          72       81       +9     
  Lines        8827     9698     +871     
  Branches     8555     9426     +871     
==========================================
+ Hits         7286     7827     +541     
- Misses       1098     1393     +295     
- Partials      443      478      +35     
Flag Coverage Δ
python 81.61% <ø> (ø)
rust 80.68% <66.80%> (-1.90%) ⬇️

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.

@aborgna-q aborgna-q changed the title refactor: More flexible pytket encoding refactor!: More flexible pytket encoding May 22, 2025
@aborgna-q
Copy link
Collaborator Author

The blackbox encoding path is missing some test coverage.
I plan to include roundtrip tests in the decoding PR.

@aborgna-q aborgna-q marked this pull request as ready for review May 22, 2025 09:56
@aborgna-q aborgna-q requested a review from a team as a code owner May 22, 2025 09:56
@aborgna-q aborgna-q requested a review from cqc-alec May 22, 2025 09:56
&self,
typ: &hugr::types::CustomType,
) -> Result<Option<RegisterCount>, Tk1ConvertError<<H>::Node>> {
match typ.name().as_str() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we be converting bools to bits as well?

.iter()
.map(|c| serde_json::to_string(c).unwrap())
.collect();
for b in &b.commands {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shouldn't we be iterating over a.commands here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch -.-'

We actually cannot compare commands directly, since the arguments names may change depending on the encoding toposort.
I added a partial comparison instead.

///
/// ## Arguments
///
/// - `node`: The HUGR for which to emit the command. Qubits and bits are
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
/// - `node`: The HUGR for which to emit the command. Qubits and bits are
/// - `node`: The HUGR node for which to emit the command. Qubits and bits are

?

}
if out_params.len() != total_out_count.params {
return Err(Tk1ConvertError::custom(format!(
"Not enough parameters in the input values for a {}. Expected {} but got {}.",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could it be too many?

///
/// - `node`: The node to register the outputs for.
/// - `circ`: The circuit containing the node.
/// - `qubit_values`: An iterator of existing qubit ids to re-use for the output.
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
/// - `qubit_values`: An iterator of existing qubit ids to re-use for the output.
/// - `qubits`: An iterator of existing qubit ids to re-use for the output.

// Check that we got the expected number of outputs.
if out_params.len() != total_out_count.params {
return Err(Tk1ConvertError::custom(format!(
"Not enough parameters in the input values for a {}. Expected {} but got {}.",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could it be too many?

};
// Update the values in the node's outputs.
//
// We preserve the order of linear values in thpe input
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
// We preserve the order of linear values in thpe input
// We preserve the order of linear values in the input

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.

3 participants