Consolidate Target mappings#15349
Conversation
|
One or more of the following people are relevant to this code:
|
Pull Request Test Coverage Report for Build 21007639926Details
💛 - Coveralls |
The following commits remove the redundant mappings `gate_name_map` and `angle_bounds` in favor of one mapping with a struct that contains all of that data. This results in a much lighter `Target` going from 504 (0x1F8) bytes to 288 bytes. The new struct called `TargetProperties` is currently internal-only. And contains three fields. - `properties` containing the mapping of the qargs and instruction properties (originally the `gate_map`). - `instruction` containing the `TargetOperation` (originally the `gate_name_map`). - `angle_bounds`: Optionally containing the angle bounds for the ``Target`` (originally `angle_bounds`.) This is the first effort to more efficiently use the ``Target`` in Rust with upcoming efforts focusing on its behavior on Python.
33af41d to
b74ea21
Compare
mtreinish
left a comment
There was a problem hiding this comment.
Overall I really like the structural change here, it reduces the weight of the target more than you pointed out in the PR summary message. That only looks mechanically at the data in the struct itself, not the allocations being pointed too. I expect for a typical target this will shrink the memory footprint more substantially. You've eliminated 2 string copies per instruction in the target. Did you run any benchmarks with this to see if it improved things at all? Also did we have an issue about reducing the string copies in the target at one point? If we did we should mark this as closing it.
I just had a few inline comments and questions.
| global_operations: HashMap<u32, HashSet<String>>, | ||
| qarg_gate_map: HashMap<Qargs, HashSet<String>>, |
There was a problem hiding this comment.
I assume these were an indexmap previously for deterministic iteration order which would have been important for Python backwards compatibility (these were originally python dicts I think). Are these not exposed directly in the public api for the target, or were there no methods to iterate them and that's why it's safe to change?
There was a problem hiding this comment.
Yes, in most cases I noticed the iteration was either internal only for classification, or the iteration was exposed to Python as a set, which in Python is unordered. For example, the Target::qargs method which is the only one exposed to python that iterates through one of these maps qarg_gate_map, wraps everything inside of a set, while the rust version returns an iterator.
| pub qubit_properties: Option<Vec<QubitProperties>>, | ||
| #[pyo3(get, set)] | ||
| pub concurrent_measurements: Option<Vec<Vec<PhysicalQubit>>>, | ||
| gate_map: GateMap, |
There was a problem hiding this comment.
I'm very happy to see this type alias go away, I always found the type aliases got in the way, and seeing that this is an Indexmap with the types is much easier to reason about.
| props_map = IndexMap::from_iter([(Qargs::Global, None)]); | ||
| } | ||
| let properties = match instruction { | ||
| TargetOperation::Variadic(_) => IndexMap::from_iter([(Qargs::Global, None)]), |
There was a problem hiding this comment.
I think this would work and be a bit more concise:
| TargetOperation::Variadic(_) => IndexMap::from_iter([(Qargs::Global, None)]), | |
| TargetOperation::Variadic(_) => indexmap!{Qargs::Global => None}, |
see: https://docs.rs/indexmap/latest/indexmap/macro.indexmap.html
There was a problem hiding this comment.
I tried doing this but it doesn't seem to work too well currently because of two reasons:
indexmap! {}uses the default hasher and here we require usage ofahash::RandomState.indexmap_with_default! {}wraps the custom hasher inside of aBuildHasherDefaultwhich doesn't evaluate to the same types here and results in the compiler yelling about not evaluating the match arm correctly.
expected IndexMap<{unknown}, {unknown}, BuildHasherDefault<RandomState>>, found IndexMap<Qargs, Option<InstructionProperties>, RandomState>
| if qarg_slice.len() != instruction.num_qubits() as usize { | ||
| return Err(TargetError::QargsMismatch { | ||
| instruction: name, | ||
| instruction: name.to_string(), |
There was a problem hiding this comment.
I know I'm the one always going on about using &str in rust interfaces. But I wonder if in this case passing an owned string to this inner method might be a better choice. Then we could avoid this copy. If you have the python method take an owned string, it shouldn't need a clone() anymore since everything else in the python add_instruction() should be by reference after this change. So if you took an owned string in from python and moved it here we'd avoid a clone() or to_string() I think. For a rust/c public/outward facing interface we can still work with &str if that's more ergonomic and do the copy before calling the inner method.
There was a problem hiding this comment.
I think it would make sense since, after all we are keeping the string inside the mapping, so I agree with this assessment. That said, it might be too late to include that in this PR as it would change the input of one of the most important methods in this struct. Perhaps we might do this for a future PR I have planned.
There was a problem hiding this comment.
I'm not sure I follow, this PR hasn't merged yet how is it too late? I think most of what I was proposing was just undoing changes that this is making and removing clones/to_string calls that aren't needed with the new internal data structure.
If you're thinking about the 2.40rc1 scheduled for today I don't think we should rush to include this in the release and should just do it for 2.5.0. Even if it were ready a change to a core data model like this is not something you want to merge the day of a release, it's too risky since there is no margin to catch mistakes before release.
There was a problem hiding this comment.
Oh, I'm sorry. This PR has been open for a bit and I had forgotten about introducing the changes here. Yes I can revert it now.
|
|
||
| /// Retrieve all the gate names in the Target | ||
| // TODO: Remove once `Target` is being consumed. | ||
| #[allow(dead_code)] |
There was a problem hiding this comment.
Nothing is using it currently but I wanted to make sure that if it weren't it wouldn't be supressed by this flag.
Cryoris
left a comment
There was a problem hiding this comment.
Matt superseded the remaining comments I had, so only 1 is left 🤷🏻♂️
- Replace ambiguous type declarations with more explicit types.
- `PropsMap` to `TargetIndexMap<K,V>` == `IndexMap<K,V,RandomState>`.
- Make `TargetProperties` private.
- Remove `_raw_operation_from_name`.
- Use `IndexMap::with_capacity_and_hasher` during deserialization.
- Revert overusage of `&str`.
- In cases where the alias of `IndexMap<String, TargetProperties, ahash::RandomState>` looked too long, I matched it to the `Index` trait whose return `Output` value is already of said type.
Initial: Clean up target mappings The following commits remove the redundant mappings `gate_name_map` and `angle_bounds` in favor of one mapping with a struct that contains all of that data. This results in a much lighter `Target` going from 504 (0x1F8) bytes to 288 bytes. The new struct called `TargetProperties` is currently internal-only. And contains three fields. - `properties` containing the mapping of the qargs and instruction properties (originally the `gate_map`). - `instruction` containing the `TargetOperation` (originally the `gate_name_map`). - `angle_bounds`: Optionally containing the angle bounds for the ``Target`` (originally `angle_bounds`.) This is the first effort to more efficiently use the ``Target`` in Rust with upcoming efforts focusing on its behavior on Python. Fix: Remove clippy unused
Initial: Clean up target mappings The following commits remove the redundant mappings `gate_name_map` and `angle_bounds` in favor of one mapping with a struct that contains all of that data. This results in a much lighter `Target` going from 504 (0x1F8) bytes to 288 bytes. The new struct called `TargetProperties` is currently internal-only. And contains three fields. - `properties` containing the mapping of the qargs and instruction properties (originally the `gate_map`). - `instruction` containing the `TargetOperation` (originally the `gate_name_map`). - `angle_bounds`: Optionally containing the angle bounds for the ``Target`` (originally `angle_bounds`.) This is the first effort to more efficiently use the ``Target`` in Rust with upcoming efforts focusing on its behavior on Python. Fix: Remove clippy unused
Summary
The following commits remove the redundant mappings
gate_name_mapandangle_boundsin favor of one mapping with a struct that contains all of that data. This results in a much lighterTargetgoing from 504 (0x1F8) bytes to 288 bytes.The new struct called
TargetPropertiesis currently internal-only. And contains three fields.propertiescontaining the mapping of the qargs and instruction properties (originally thegate_map).instructioncontaining theTargetOperation(originally thegate_name_map).angle_bounds: Optionally containing the angle bounds for theTarget(originallyangle_bounds.)This is the first effort to more efficiently use the
Targetin Rust with upcoming efforts focusing on its behavior on Python.Details and comments