Make BasisTranslator rust-native.#14659
Conversation
Pull Request Test Coverage Report for Build 17215159484Warning: This coverage report may be inaccurate.This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.
Details
💛 - Coveralls |
c53b7ec to
6ccba5a
Compare
BasisTranslator rust-native.
|
There are some significant speedups:
SOME BENCHMARKS HAVE CHANGED SIGNIFICANTLY. |
0526837 to
d46bcd2
Compare
b978929 to
9081202
Compare
|
One or more of the following people are relevant to this code:
|
BasisTranslator rust-native.BasisTranslator rust-native.
- We cannot get away with not using `ParameterExpression` to create unique parameters. Until a better alternative comes around, we should stick to using it.
- As of 1.79, `LazyLock` was not yet made stable, these commits remove `LazyLock` in favor of `OnceLock`.
- Skipping the dict builds and using `Python::with_gil` avoids using python tokens when not necessary during the `replace_node` method in `run_basis_translator`.
- The dictionary in question was only really needed when an instance of `ParameterExpression` is present on the gate. So it is optionally built if an instance of Parameter expression is found.
- The last remaining usage of py tokens exists during `compose_transforms` which still builds a parameter vector.
- In `replace_node` we will first filter out `ParameterExpression` keys in the parameter map. If the collection comes out empty, we don't build the mapping.
49e7207 to
9d3aad6
Compare
| for key in param_obj.iter_symbols() { | ||
| match ¶meter_map[key].clone() { | ||
| Param::ParameterExpression(val) => { | ||
| subs_map.insert(key.clone(), val.as_ref().clone()); |
There was a problem hiding this comment.
Oh we should update subs to also take &Symbol as key in a separate PR
| } | ||
|
|
||
| _ => {} | ||
| fn param_assignment_expr( |
There was a problem hiding this comment.
Could we make this a method on Param? We could also do that in a follow-up if you prefer.
As per @Cryoris previous reviews, there is no path for a `UnitaryGate` to be represented as a key in the `EquivalenceLibrary`. Thus we can remove the unitary path from the `name_to_packed_operation` method.
- Fix comment in `recurse_circuit`. - Remove `param_assignment_global_phase` and use `param_assignment_expr. - `param_assignment_expr` now accepts a `ParameterExpression` instance. - Add `allow_complex` flag in `param_assignment_expr` to prevent complex values from getting assigned.
| .get_bound(py) | ||
| .call1((&gate_name, num_params))? | ||
| .extract()?; | ||
| let mut placeholder_params: SmallVec<[Param; 3]> = (0..num_params as u32) |
There was a problem hiding this comment.
Do these need to be mut? I don't think we change them, do we?
There was a problem hiding this comment.
We change them when a python gate is made and we re-extract the parameters from there. I can have it removed though.
There was a problem hiding this comment.
Ah right, no then they are indeed mut
| let inst = match name { | ||
| "barrier" => StandardInstruction::Barrier(num_qubits), | ||
| "delay" => StandardInstruction::Delay(qiskit_circuit::operations::DelayUnit::DT), | ||
| "measure" => StandardInstruction::Measure, | ||
| "reset" => StandardInstruction::Reset, |
There was a problem hiding this comment.
I don't think we contain translations for instructions, do we? I think we can also replace this whole block with an unreachable statement like for the unitary
There was a problem hiding this comment.
Sure! Couldn't remove this unfortunately. The specified target basis doesn't filter out the StandardInstructions.
| equiv_lib: &mut EquivalenceLibrary, | ||
| min_qubits: usize, | ||
| target: Option<&Target>, | ||
| target_basis: Option<HashSet<String>>, |
There was a problem hiding this comment.
We should take an Option<&HashSet<&str>> in the Rust interface I think, and create the correct type in the python wrapper above 🤔
There was a problem hiding this comment.
In general I agree we don't need or want an owned type here, but in practice we should never be setting this from Rust, it should always be None. In rust space we should always be using a Target and never loose constraints, this is strictly a backwards compatibility thing for Python.
It would be better if we just removed this argument from the rust code and did Target.from_configuration(basis_gates) in the Python code for the pass instead and pass the generated target to rust. That would also simplify the rust code so we only have a target to worry about.
- Remove hashset from `compose_transforms` use array too. - Fix comment in error. - Build `HashSet<&str>` from python counterpart and use as main `target_basis` argument in `run_basis_translator`.
Cryoris
left a comment
There was a problem hiding this comment.
Thanks for all the work, this looks in a good shape to me now to unblock the C transpiler function. There's some follow-ups which we can do but with the beta release in mind I would be happy to merge it 🙂
The main follow ups are:
- #14659 (comment) (this we could still do here if you want, but I'm fine doing a follow up to unblock now)
- the Rust-native errors
- putting parameter assignments as methods on
ParamandParameterExpressioninstead of standalones
* TBD
* TBD 0
* TBD: Moved most of the operations in compose_transform to be rust native.
* Fix: Revert any removals related to `ParameterExpression`.
- We cannot get away with not using `ParameterExpression` to create unique parameters. Until a better alternative comes around, we should stick to using it.
* Fix: Remove `LazyLock`.
- As of 1.79, `LazyLock` was not yet made stable, these commits remove `LazyLock` in favor of `OnceLock`.
* Fix: Remove most pytokens by avoiding building a dict.
- Skipping the dict builds and using `Python::with_gil` avoids using python tokens when not necessary during the `replace_node` method in `run_basis_translator`.
- The dictionary in question was only really needed when an instance of `ParameterExpression` is present on the gate. So it is optionally built if an instance of Parameter expression is found.
- The last remaining usage of py tokens exists during `compose_transforms` which still builds a parameter vector.
* Filter to only parameter expression when building parameter_map.
- In `replace_node` we will first filter out `ParameterExpression` keys in the parameter map. If the collection comes out empty, we don't build the mapping.
* Fix: Use rust native parameters.
* Fix: Re-add "fixedbitset"
* Fix: Convert to `Param::Float` if a real value is obtained
* Fix: Second rebase
* Add: Leverage use of rust-native `substitute_node_with_py`.
* Fix: Implement native errors for `BasisTranslator`.
- Use `Matrix::Identity` based on the provided matrices.
- Add trait `ToPyErr` that ensures any new error is converted to its respective Python counterpart.
- Remove indexing from `ParameterExpression` creation in `compose_transforms`.
- Remove incorrect coercion into value from `replace_node`.
* Add: global operation extraction from Rust.
- Modify `Target::get_non_global_operation_names` to not return an option.
* Fix: Remove unused import in python `BasisTranslator`.
* Refactor: `ToPyError` to `From<BasisTranslatorError> for PyErr`.
* Chore: Rename name set in `compose_transforms`.
- Use acquired gil for `to_string() call of `PyErr`.
* Fix: Take `&mut DAGCircuit` in `BasisTranslator`.
* Fix: Address rest of review comments
Co-authored-by: Julien Gacon <gaconju@gmail.com>
* Refactor: Use `&str` over `String` when possible.
* Chore: Round of review comments.
* Fix: More review comments
- Use rust native dag_to_circuit method and convert to `QuantumCircuit` for control flow procedures.
- Add `num_symbols` to `ParameterExpression`.
- Pass `allow_unknwon_parameters` as true for `bind` and `subs` in `BasisTranslator`.
Co-authored-by: Julien Gacon <gaconju@gmail.com>
* Fix: Split out parameter assignment from `replace_node`
* Fix: More efficient parameter binding
Co-authored-by: Julien Gacon <gaconju@gmail.com>
* Remove: Path for unitaries.
As per @Cryoris previous reviews, there is no path for a `UnitaryGate` to be represented as a key in the `EquivalenceLibrary`. Thus we can remove the unitary path from the `name_to_packed_operation` method.
* Fix: Remove most string conversions from `BasisTranslatorError`.
* Fix: Address first wave of review items
- Fix comment in `recurse_circuit`.
- Remove `param_assignment_global_phase` and use `param_assignment_expr.
- `param_assignment_expr` now accepts a `ParameterExpression` instance.
- Add `allow_complex` flag in `param_assignment_expr` to prevent complex values from getting assigned.
* Refactor: Use `ParameterError` as a base for `BasisTranslatorError`.
* FIx: Remove `BasisGateError`, use `.expect()` for the rest.
* Fix: Final review comments?
- Remove hashset from `compose_transforms` use array too.
- Fix comment in error.
- Build `HashSet<&str>` from python counterpart and use as main `target_basis` argument in `run_basis_translator`.
* Fix: Re-add removed parameter extraction
---------
Co-authored-by: Julien Gacon <gaconju@gmail.com>
* TBD
* TBD 0
* TBD: Moved most of the operations in compose_transform to be rust native.
* Fix: Revert any removals related to `ParameterExpression`.
- We cannot get away with not using `ParameterExpression` to create unique parameters. Until a better alternative comes around, we should stick to using it.
* Fix: Remove `LazyLock`.
- As of 1.79, `LazyLock` was not yet made stable, these commits remove `LazyLock` in favor of `OnceLock`.
* Fix: Remove most pytokens by avoiding building a dict.
- Skipping the dict builds and using `Python::with_gil` avoids using python tokens when not necessary during the `replace_node` method in `run_basis_translator`.
- The dictionary in question was only really needed when an instance of `ParameterExpression` is present on the gate. So it is optionally built if an instance of Parameter expression is found.
- The last remaining usage of py tokens exists during `compose_transforms` which still builds a parameter vector.
* Filter to only parameter expression when building parameter_map.
- In `replace_node` we will first filter out `ParameterExpression` keys in the parameter map. If the collection comes out empty, we don't build the mapping.
* Fix: Use rust native parameters.
* Fix: Re-add "fixedbitset"
* Fix: Convert to `Param::Float` if a real value is obtained
* Fix: Second rebase
* Add: Leverage use of rust-native `substitute_node_with_py`.
* Fix: Implement native errors for `BasisTranslator`.
- Use `Matrix::Identity` based on the provided matrices.
- Add trait `ToPyErr` that ensures any new error is converted to its respective Python counterpart.
- Remove indexing from `ParameterExpression` creation in `compose_transforms`.
- Remove incorrect coercion into value from `replace_node`.
* Add: global operation extraction from Rust.
- Modify `Target::get_non_global_operation_names` to not return an option.
* Fix: Remove unused import in python `BasisTranslator`.
* Refactor: `ToPyError` to `From<BasisTranslatorError> for PyErr`.
* Chore: Rename name set in `compose_transforms`.
- Use acquired gil for `to_string() call of `PyErr`.
* Fix: Take `&mut DAGCircuit` in `BasisTranslator`.
* Fix: Address rest of review comments
Co-authored-by: Julien Gacon <gaconju@gmail.com>
* Refactor: Use `&str` over `String` when possible.
* Chore: Round of review comments.
* Fix: More review comments
- Use rust native dag_to_circuit method and convert to `QuantumCircuit` for control flow procedures.
- Add `num_symbols` to `ParameterExpression`.
- Pass `allow_unknwon_parameters` as true for `bind` and `subs` in `BasisTranslator`.
Co-authored-by: Julien Gacon <gaconju@gmail.com>
* Fix: Split out parameter assignment from `replace_node`
* Fix: More efficient parameter binding
Co-authored-by: Julien Gacon <gaconju@gmail.com>
* Remove: Path for unitaries.
As per @Cryoris previous reviews, there is no path for a `UnitaryGate` to be represented as a key in the `EquivalenceLibrary`. Thus we can remove the unitary path from the `name_to_packed_operation` method.
* Fix: Remove most string conversions from `BasisTranslatorError`.
* Fix: Address first wave of review items
- Fix comment in `recurse_circuit`.
- Remove `param_assignment_global_phase` and use `param_assignment_expr.
- `param_assignment_expr` now accepts a `ParameterExpression` instance.
- Add `allow_complex` flag in `param_assignment_expr` to prevent complex values from getting assigned.
* Refactor: Use `ParameterError` as a base for `BasisTranslatorError`.
* FIx: Remove `BasisGateError`, use `.expect()` for the rest.
* Fix: Final review comments?
- Remove hashset from `compose_transforms` use array too.
- Fix comment in error.
- Build `HashSet<&str>` from python counterpart and use as main `target_basis` argument in `run_basis_translator`.
* Fix: Re-add removed parameter extraction
---------
Co-authored-by: Julien Gacon <gaconju@gmail.com>
…slator`. - A well known bug introduced by Qiskit#14659 in which the `AerSimulator` is known to define `UnitaryGate` as part of its target basis. However, `BasisTranslator` should not include `UnitaryGate` as part of its calculations, it should instead be handled by `UnitarySynthesis`. - To fix this, `BasisTranslator` skips any `UnitaryGate` in the circuit by filtering its `op_nodes` to check for any `UnitaryGate` instances. - The only question left is whether we should still have the panic condition in `name_to_packed_operation` because this check is done on a name basis rather than instance.
…slator`. - A well known bug introduced by Qiskit#14659 in which the `AerSimulator` is known to define `UnitaryGate` as part of its target basis. However, `BasisTranslator` should not include `UnitaryGate` as part of its calculations, it should instead be handled by `UnitarySynthesis`. - To fix this, `BasisTranslator` skips any `UnitaryGate` in the circuit by filtering its `op_nodes` to check for any `UnitaryGate` instances. - The only question left is whether we should still have the panic condition in `name_to_packed_operation` because this check is done on a name basis rather than instance.
Summary
The following commits expose a more rust native
BasisTranslatorthat operates mostly on the Rust level by taking the following into account:Targetin Rust-space instead of Python.ParameterExpressionto correctly handle parameters.BasisTranslatorErrorto correclty represent errors without having to go through Python.Details and comments
Serves as the prelude for #14874.
This PR is currently built on top of #14799 and needs to be rebased once it merges. Rebased now with #14766REBASED!Pre-requisites:
substitute_node_with_dagto DAGCircuit #14766