Add Split2qUnitaries to C API#14720
Conversation
|
One or more of the following people are relevant to this code:
|
Pull Request Test Coverage Report for Build 17153328607Details
💛 - Coveralls |
0e582b7 to
d1f83e7
Compare
This commit adds a standalone transpiler pass function to the C API for running the Split2qUnitaries transpiler pass. This pass mutates the dag in place if there is no swap splitting that occurs (either by user option or because there are no applicable gates) or returns a new dag with a permutation. This result is encapsulated in a Split2qUnitariesResult struct that contains the circuit and the permutation. One small change was needed to the dag circuit and the rust code of the pass to enable running it without python. A py token was used for updating the instruction cache when splitting instructions. This was mostly a leftover from when unitary gate was a PyGate where this mattered, but now there isn't anything to prepopulate. But also it wasn't ever necessary in the case of a python defined operation it would do the ref count update implicitly and populate the cache on the first access. This commit removes the python usage for this which simplifies the code and also enables running this pass from standalone C. Fixes Qiskit#14448
|
This is ready for review now, I've rebased it on main now that the pre-reqs have merged and added the missing test coverage. |
jakelishman
left a comment
There was a problem hiding this comment.
I stopped looking through the C tests at the point of the last comment, but I suspect it'd just be the same comments in the last test too.
| /// The result from ``qk_transpiler_pass_standalone_split_2q_unitaries()``. | ||
| pub struct Split2qUnitariesResult { | ||
| circuit: CircuitData, | ||
| permutation: Vec<u32>, | ||
| } |
There was a problem hiding this comment.
This will interact awkwardly with the new push to having TranspileLayout always store the output permutation. Perhaps we want to do a similar thing with this pass as we're doing for SabreLayout, but it's starting to get a bit awkward about whether we want to expose a stand-alone way of calling the pass if we can't either a) pass a TranspileLayout from pass to pass or b) provide methods on TranspileLayout in C to update the tracking data based on new changes.
I feel like our options are going to be:
- don't bother with standalone functions like this one until we have a better answer for exactly what we'll do
- claim "standalone passes can't be composed because you don't get the full pipeline tracking" so the current API is fine
- extend the C API such that you could reasonably use the methods sequentially to emulate the Qiskit pipeline, just with loads of extra
dag_to_circuit(do_pass(circuit_to_dag(qc)))everywhere
Personally I'd tend towards either the first or second options - I don't think the standalone passes are necessarily all super high priority. I think they're useful as a research direction, and for incrementally moving us towards full exposure to C, so that's an argument in favour of point 2 over point 1.
There was a problem hiding this comment.
I think we've discussed this in several other PRs, and settled on option 2. We'll want to figure out how to do this as part of exposing composable transpilation pipelines in C.
| let result = unsafe { const_ptr_as_ref(result) }; | ||
| result.permutation.as_ptr() |
There was a problem hiding this comment.
Do we actually have to produce the &Split2qUnitariesResult here? I vaguely thought we could just unsafe { result.permutation }.as_ptr(), with all the undefined behaviour that goes with that.
I'm totally cool if we deliberately want explicit panics on bad accesses, though.
There was a problem hiding this comment.
This was changed in: ae02b37 it returns a null pointer if there is no permutation and a pointer to a transpile layout if there is one.
| /// qk_circuit_gate(qc, QkGate_CX, qargs, NULL); | ||
| /// } | ||
| /// } | ||
| /// QkSplit2qUnitariesResult *result = qk_transpiler_pass_standalone_split_2q_unitaries(qc, 1.0 - 1e-16, true) |
There was a problem hiding this comment.
It's probably neither here nor there, but the closest double-precision floating-point value to 1.0 - 1e-16 is actually more like 1.11e-16 - saying 1e-16 is actually kind of wrong by over 10%. Are we actually able to hit a precision that's just 1ULP?
| #[cfg(feature = "cache_pygates")] | ||
| let py_op = match new_op.view() { | ||
| OperationRef::StandardGate(_) | ||
| | OperationRef::StandardInstruction(_) | ||
| | OperationRef::Unitary(_) => OnceLock::new(), | ||
| OperationRef::Gate(gate) => OnceLock::from(gate.gate.clone_ref(py)), | ||
| OperationRef::Instruction(instruction) => { | ||
| OnceLock::from(instruction.instruction.clone_ref(py)) | ||
| } | ||
| OperationRef::Operation(op) => OnceLock::from(op.operation.clone_ref(py)), | ||
| }; | ||
| let inst = PackedInstruction { | ||
| op: new_op, | ||
| qubits: self.qargs_interner.insert_owned(qubits), | ||
| clbits: self.cargs_interner.get_default(), | ||
| params: (!params.is_empty()).then(|| Box::new(params)), | ||
| label: None, | ||
| #[cfg(feature = "cache_pygates")] | ||
| py_op, | ||
| py_op: OnceLock::new(), |
There was a problem hiding this comment.
This might have been better as a standalone patch, but let's not bother now.
There was a problem hiding this comment.
Heh, this I think is the reason this is needed for #14760
raynelfss
left a comment
There was a problem hiding this comment.
This is looking good, I just have a couple of comments.
raynelfss
left a comment
There was a problem hiding this comment.
This looks about ready, just a couple small comments.
Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com>
jakelishman
left a comment
There was a problem hiding this comment.
Other than minor stuff, this is fine by me.
jakelishman
left a comment
There was a problem hiding this comment.
Thanks for the changes - I think this is fine now (though tbh, I've looked at it enough times that my eyes are kind of glossing over). I'll let Ray press the merge button if he's happy too.
* Add Split2qUnitaries to C API This commit adds a standalone transpiler pass function to the C API for running the Split2qUnitaries transpiler pass. This pass mutates the dag in place if there is no swap splitting that occurs (either by user option or because there are no applicable gates) or returns a new dag with a permutation. This result is encapsulated in a Split2qUnitariesResult struct that contains the circuit and the permutation. One small change was needed to the dag circuit and the rust code of the pass to enable running it without python. A py token was used for updating the instruction cache when splitting instructions. This was mostly a leftover from when unitary gate was a PyGate where this mattered, but now there isn't anything to prepopulate. But also it wasn't ever necessary in the case of a python defined operation it would do the ref count update implicitly and populate the cache on the first access. This commit removes the python usage for this which simplifies the code and also enables running this pass from standalone C. Fixes Qiskit#14448 * Pivot to returning TranspileLayout instead of custom return type * Fix memory leak in test * Add missing newlines from error strings * Apply suggestions from code review Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com> * Fix docstring example * Fix error message on internal pass failure * Add missing QkTranspileLayout free in failure mode where it shouldn't exist * Fix failure message in test where permutation doesn't exist and is expected --------- Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com>
* Add Split2qUnitaries to C API This commit adds a standalone transpiler pass function to the C API for running the Split2qUnitaries transpiler pass. This pass mutates the dag in place if there is no swap splitting that occurs (either by user option or because there are no applicable gates) or returns a new dag with a permutation. This result is encapsulated in a Split2qUnitariesResult struct that contains the circuit and the permutation. One small change was needed to the dag circuit and the rust code of the pass to enable running it without python. A py token was used for updating the instruction cache when splitting instructions. This was mostly a leftover from when unitary gate was a PyGate where this mattered, but now there isn't anything to prepopulate. But also it wasn't ever necessary in the case of a python defined operation it would do the ref count update implicitly and populate the cache on the first access. This commit removes the python usage for this which simplifies the code and also enables running this pass from standalone C. Fixes Qiskit#14448 * Pivot to returning TranspileLayout instead of custom return type * Fix memory leak in test * Add missing newlines from error strings * Apply suggestions from code review Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com> * Fix docstring example * Fix error message on internal pass failure * Add missing QkTranspileLayout free in failure mode where it shouldn't exist * Fix failure message in test where permutation doesn't exist and is expected --------- Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com>
Summary
This commit adds a standalone transpiler pass function to the C API for
running the Split2qUnitaries transpiler pass. This pass mutates the dag
in place if there is no swap splitting that occurs (either by user
option or because there are no applicable gates) or returns a new dag
with a permutation. This result is encapsulated in a
Split2qUnitariesResult struct that contains the circuit and the
permutation.
One small change was needed to the dag circuit and the rust code of the
pass to enable running it without python. A py token was used for
updating the instruction cache when splitting instructions. This was
mostly a leftover from when unitary gate was a PyGate where this
mattered, but now there isn't anything to prepopulate. But also it
wasn't ever necessary in the case of a python defined operation it would
do the ref count update implicitly and populate the cache on the first
access. This commit removes the python usage for this which simplifies
the code and also enables running this pass from standalone C.
Details and comments
Fixes #14448
TODO:
This PR is based on top of #14668 will need to be rebased once #14668 merges. In the meantime you can review just the contents of this PR by looking at the HEAD commit on the PR: 0e582b7This has been rebased now