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

refactor: Cleanup tket1 serialized op structures #419

Merged
merged 9 commits into from
Jun 20, 2024
Merged

Conversation

aborgna-q
Copy link
Collaborator

This is a noisy internal refactor of ::serialize::pytket::op::JsonOp, extracted from the work towards #379.

JsonOp was a temporary structure used during the encoding/decoding of pytket circuits that represented two different kinds of operation:

  • pytket operations with a direct tket2 counterpart
  • other operations that have to be encoded as OpaqueOps

This mixed up the two definitions, and made applying custom logic to one of the variants more annoying.
(E.g. the special handling of bit input/outputs for tket2 ops needed for #379).

This PR splits the structs into a Native and an Opaque variant, so we can keep the implementation clean. The code is functionally the same.

@aborgna-q aborgna-q requested a review from doug-q June 19, 2024 14:18
Copy link

codecov bot commented Jun 19, 2024

Codecov Report

Attention: Patch coverage is 84.34505% with 49 lines in your changes missing coverage. Please review.

Project coverage is 81.33%. Comparing base (8c5a487) to head (58b5966).

Files Patch % Lines
tket2/src/serialize/pytket/op/serialised.rs 80.88% 8 Missing and 5 partials ⚠️
tket2/src/serialize/pytket/op/native.rs 92.07% 7 Missing and 1 partial ⚠️
tket2/src/serialize/pytket/decoder.rs 86.00% 4 Missing and 3 partials ⚠️
tket2/src/serialize/pytket/op.rs 77.41% 6 Missing and 1 partial ⚠️
tket2/src/ops.rs 55.55% 4 Missing ⚠️
tket2/src/serialize/pytket/encoder.rs 86.66% 1 Missing and 3 partials ⚠️
tket2/src/serialize/pytket.rs 0.00% 1 Missing and 2 partials ⚠️
tket2/src/passes/commutation.rs 33.33% 0 Missing and 2 partials ⚠️
tket2/src/passes/pytket.rs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #419      +/-   ##
==========================================
+ Coverage   81.00%   81.33%   +0.33%     
==========================================
  Files          57       59       +2     
  Lines        5411     5423      +12     
  Branches     4926     4938      +12     
==========================================
+ Hits         4383     4411      +28     
+ Misses        819      793      -26     
- Partials      209      219      +10     
Flag Coverage Δ
python 97.52% <ø> (ø)
rust 79.74% <84.34%> (+0.37%) ⬆️

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.

@aborgna-q
Copy link
Collaborator Author

I fixed the CI error.
The one functional change I made was adding .with_extension_delta(TKET1_EXTENSION_ID) to the decoded circuit signature, and that's a bit untested in the rest of the code :P

Copy link
Contributor

@doug-q doug-q left a comment

Choose a reason for hiding this comment

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

Looks much better. I've not looked at coverage lines yet, that may induce further comments.

tket2/src/ops.rs Outdated
}

// Internal implementation for `TryFrom<Optype> for Tk2Op` that doesn't copy the `OpType` when it errors.
fn optype_to_tk2op(op: &OpType) -> Option<Tk2Op> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Both callsites create the same error anyway, I would prefer to do it once here. Not a big deal though.


/// A serialized operation, containing the operation type and all its attributes.
/// An operation originated from pytket, containing the operation type and all its attributes.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// An operation originated from pytket, containing the operation type and all its attributes.
/// An operation originating from pytket, containing the operation type and all its attributes.

op.compute_param_fields();
Some(op)

tk1_op.ok_or_else(|| OpConvertError::UnsupportedOpSerialization(op))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
tk1_op.ok_or_else(|| OpConvertError::UnsupportedOpSerialization(op))
tk1_op.ok_or(OpConvertError::UnsupportedOpSerialization(op))

Comment on lines 54 to 55
/// Requires specifying the number of qubits and bits in the operation in
/// case the `serial_op` does not define a signature.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// Requires specifying the number of qubits and bits in the operation in
/// case the `serial_op` does not define a signature.
/// If `serial_op` defines a signature then `num_qubits` and `num_qubits` are ignored. Otherwise, a signature is synthesised from those parameters.

param_inputs,
num_params,
/// Get the hugr optype for the operation.
pub fn optype(&self) -> OpType {
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps this should take self by value, since it unconditionally clones. I am not sure whether this would work having not inspected all methods used below.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The Opaque path doesn't consume the json_op, so we avoid a clone by using &self.

But Native is the other way, returning an Optype consumes it.

I'll define both options, so the caller can choose.

// TODO: Throw an error? We should never get here if the name matches.
return None;
};
let op = serde_yaml::from_value(arg.value.clone()).ok()?;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we return a Result? Swallowing this error is a bit gross.


/// Compute the signature of the operation.
///
/// We assume the operation has `num_qubits` qubit inputs and outputs,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// We assume the operation has `num_qubits` qubit inputs and outputs,
/// The signature returned has `num_qubits` qubit inputs, followed by `num_bits` bit inputs, followed by `num_params` `f64` inputs. It has `num_qubits` qubit outputs follwed by `num_bits` bit outputs.


/// Wraps the op into a [`TKET1_OP_NAME`] opaque operation.
pub fn as_custom_op(&self) -> CustomOp {
let op = serde_yaml::to_value(self).unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

I have looked at all think these unwraps and I think they're ok.

Comment on lines 158 to 166
//let sig = self.signature();
//OpaqueOp::new(
// TKET1_EXTENSION_ID,
// TKET1_OP_NAME,
// "".into(),
// vec![payload],
// sig,
//)
//.into()
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
//let sig = self.signature();
//OpaqueOp::new(
// TKET1_EXTENSION_ID,
// TKET1_OP_NAME,
// "".into(),
// vec![payload],
// sig,
//)
//.into()

"An opaque TKET1 operation.".into(),
JsonOpSignature([json_op_payload])
JsonOpSignature([tket1_op_payload])
Copy link
Contributor

Choose a reason for hiding this comment

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

should we rename JsonOpSignature?

@doug-q
Copy link
Contributor

doug-q commented Jun 20, 2024

I suggest grepping for JsonOp in case there are any other dangling references.

}

/// Consumes the `NativeOp` and returns the underlying `OpType`.
pub fn into_op(self) -> OpType {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pub fn into_op(self) -> OpType {
pub fn into_optype(self) -> OpType {

@aborgna-q aborgna-q requested a review from doug-q June 20, 2024 10:32
Copy link
Contributor

@doug-q doug-q left a comment

Choose a reason for hiding this comment

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

Just a couple of nits, and one suspicious coverage gap. Happy to approve if you don't think it's a problem.

tket2/src/ops.rs Outdated
}
}

// Internal implementation for `TryFrom<Optype> for Tk2Op` that doesn't copy the `OpType` when it errors.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Internal implementation for `TryFrom<Optype> for Tk2Op` that doesn't copy the `OpType` when it errors.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah, the original function that returned an Option was meant as an optimisation to avoid cloning the optype on the error path of TryFrom<OpType>. I guess it's too much of a micro optimisation, so I'll just leave the TryFrom<&OpType impl as we had before.

@@ -169,16 +148,44 @@ pub fn save_tk1_json_str(circ: &Circuit) -> Result<String, TK1ConvertError> {

/// Error type for conversion between `Op` and `OpType`.
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum OpConvertError {
/// The serialized operation is not supported.
Copy link
Contributor

Choose a reason for hiding this comment

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

these comments duplicating the error messages right next to them are very annoying. 😠 . No action required on your part.

if ty == &QB_T {
num_qubits += 1
} else if *ty == *LINEAR_BIT {
num_bits += 1
Copy link
Contributor

Choose a reason for hiding this comment

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

this line seems to be missing coverage? somewhat concerning.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We don't support native measurements yet (that's coming in the PR eliminating linear bits), so this path is never taken.
There's a test with a pytket measurement, but that gets encoded as an opaque op instead.

///
/// # Errors
///
/// Returns a
Copy link
Contributor

Choose a reason for hiding this comment

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

incomplete comment

Copy link
Contributor

Choose a reason for hiding this comment

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

@aborgna-q I think you missed this?

Copy link
Contributor

Choose a reason for hiding this comment

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

Nope, I can't work computers. It's perfect

@aborgna-q aborgna-q requested a review from doug-q June 20, 2024 12:08
@aborgna-q aborgna-q added this pull request to the merge queue Jun 20, 2024
Merged via the queue into main with commit a46e63f Jun 20, 2024
16 checks passed
@aborgna-q aborgna-q deleted the ab/rework-jsonop branch June 20, 2024 12:26
@hugrbot hugrbot mentioned this pull request Jun 18, 2024
@hugrbot hugrbot mentioned this pull request Aug 1, 2024
github-merge-queue bot pushed a commit that referenced this pull request Aug 1, 2024
## 🤖 New release
* `tket2`: 0.1.0-alpha.2 -> 0.1.0
* `tket2-hseries`: 0.1.0

<details><summary><i><b>Changelog</b></i></summary><p>

## `tket2`
<blockquote>

##
[0.1.0](tket2-v0.1.0-alpha.2...tket2-v0.1.0)
- 2024-08-01

### Bug Fixes
- Single source of truth for circuit names, and better circuit errors
([#390](#390))
- Support non-DFG circuits
([#391](#391))
- Portmatching not matching const edges
([#444](#444))
- Pattern matcher discriminating on opaqueOp description
([#441](#441))
- `extract_dfg` inserting the output node with an invalid child order
([#442](#442))
- Recompile ecc sets after
[#441](#441)
([#484](#484))

### Documentation
- Update tket2-py readme
([#431](#431))
- Better error reporting in portmatching
([#437](#437))
- Improved multi-threading docs for Badger
([#495](#495))

### New Features
- `Circuit::operations` ([#395](#395))
- tuple unpack rewrite ([#406](#406))
- guppy → pytket conversion
([#407](#407))
- Drop linear bits, improve pytket encoding/decoding
([#420](#420))
- *(py)* Allow using `Tk2Op`s in the builder
([#436](#436))
- Initial support for `TailLoop` as circuit parent
([#417](#417))
- Support tuple unpacking with multiple unpacks
([#470](#470))
- Partial tuple unpack ([#475](#475))
- [**breaking**] Compress binary ECCs using zlib
([#498](#498))
- Add timeout options and stats to Badger
([#496](#496))
- Expose advanced Badger timeout options to tket2-py
([#506](#506))

### Refactor
- [**breaking**] Simplify tket1 conversion errors
([#408](#408))
- Cleanup tket1 serialized op structures
([#419](#419))

### Testing
- Add coverage for Badger split circuit multi-threading
([#505](#505))
</blockquote>

## `tket2-hseries`
<blockquote>

##
[0.1.0](https://github.com/CQCL/tket2/releases/tag/tket2-hseries-v0.1.0)
- 2024-08-01

### New Features
- [**breaking**] init tket2-hseries
([#368](#368))
- *(tket2-hseries)* Add `tket2.futures` Hugr extension
([#471](#471))
- Add lazify-measure pass
([#482](#482))
- add results extensions
([#494](#494))
- *(tket2-hseries)* [**breaking**] Add `HSeriesPass`
([#487](#487))
</blockquote>


</p></details>

---
This PR was generated with
[release-plz](https://github.com/MarcoIeni/release-plz/).

---------

Co-authored-by: Douglas Wilson <[email protected]>
@hugrbot hugrbot mentioned this pull request Aug 1, 2024
github-merge-queue bot pushed a commit that referenced this pull request Aug 15, 2024
## 🤖 New release
* `tket2`: 0.1.0 -> 0.1.1
* `tket2-hseries`: 0.1.0 -> 0.1.1

<details><summary><i><b>Changelog</b></i></summary><p>

## `tket2`
<blockquote>

##
[0.1.0](tket2-v0.1.0-alpha.2...tket2-v0.1.0)
- 2024-08-01

### Bug Fixes
- Single source of truth for circuit names, and better circuit errors
([#390](#390))
- Support non-DFG circuits
([#391](#391))
- Portmatching not matching const edges
([#444](#444))
- Pattern matcher discriminating on opaqueOp description
([#441](#441))
- `extract_dfg` inserting the output node with an invalid child order
([#442](#442))
- Recompile ecc sets after
[#441](#441)
([#484](#484))

### Documentation
- Update tket2-py readme
([#431](#431))
- Better error reporting in portmatching
([#437](#437))
- Improved multi-threading docs for Badger
([#495](#495))

### New Features
- `Circuit::operations` ([#395](#395))
- tuple unpack rewrite ([#406](#406))
- guppy → pytket conversion
([#407](#407))
- Drop linear bits, improve pytket encoding/decoding
([#420](#420))
- *(py)* Allow using `Tk2Op`s in the builder
([#436](#436))
- Initial support for `TailLoop` as circuit parent
([#417](#417))
- Support tuple unpacking with multiple unpacks
([#470](#470))
- Partial tuple unpack ([#475](#475))
- [**breaking**] Compress binary ECCs using zlib
([#498](#498))
- Add timeout options and stats to Badger
([#496](#496))
- Expose advanced Badger timeout options to tket2-py
([#506](#506))

### Refactor
- [**breaking**] Simplify tket1 conversion errors
([#408](#408))
- Cleanup tket1 serialized op structures
([#419](#419))

### Testing
- Add coverage for Badger split circuit multi-threading
([#505](#505))
</blockquote>

## `tket2-hseries`
<blockquote>

##
[0.1.1](tket2-hseries-v0.1.0...tket2-hseries-v0.1.1)
- 2024-08-15

### New Features
- *(tket2-hseries)* make result operation internals public
([#542](#542))
</blockquote>


</p></details>

---
This PR was generated with
[release-plz](https://github.com/MarcoIeni/release-plz/).

---------

Co-authored-by: Seyon Sivarajah <[email protected]>
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