-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Functionality to list operations in QuantumCircuit with associated Qubits #13055
base: main
Are you sure you want to change the base?
Conversation
Thank you for opening a new pull request. Before your PR can be merged it will first need to pass continuous integration tests and be reviewed. Sometimes the review process can be slow, so please be patient. While you're waiting, please feel free to review other open PRs. While only a subset of people are authorized to approve pull requests for merging, everyone is encouraged to review open pull requests. Doing reviews helps reduce the burden on the core team and helps make the project's code better for everyone. One or more of the following people are relevant to this code:
|
Hi @mtreinish ,
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for taking the issue and getting an implementation started. I left a few inline comments. But I think my biggest concern right now is about the return type. Write now you're returning a Vec<Option<String>, Option<String>>
, but instead I think it should be a Vec<HashMap<&str, usize>>
where the vec index is the qubit index and the hashmap is gate name mapped to count. I had some other inline comments, but refactoring the code to adjust the return type will take those into account.
/// Returns: | ||
/// list. | ||
#[pyo3(signature = ())] | ||
pub fn operations_with_qubits(&self, py: Python<'_>) -> PyResult<Py<PyAny>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of returning a Py<PyAny>
here typically I'd prefer to return the Vec
directly because then this function would be callable from rust too. The pyo3 macros build a parallel c ffi function for use that is what python is actually calling, so this is still usable rust function otherwise. If we return a Vec here the macro will do the pyobject conversion for us when called from python.
/// list. | ||
#[pyo3(signature = ())] | ||
pub fn operations_with_qubits(&self, py: Python<'_>) -> PyResult<Py<PyAny>> { | ||
let mut ret: Vec<(Option<String>, Option<String>)> = vec![]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I think the return type here is a bit off. What I think would be useful here is to return either a vec or hashmap of qubit indices that map to the instruction counts on them. For example something like:
[
{"h": 20, "cx" 2, "s": 10},
{"cx": 2},
]
for a circuit that has 2 qubits with 20 hadamards, 10 s gates, and 2 cx gates on qubit 0 and qubit 1 has 2 cx gates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hi @mtreinish , I'm a bit stuck here, how could I get this information (the qubit numbers) without having to get substrings on the string gotten from the Qubit objects from 'Some(self.qubits.get(*b).unwrap().to_string()'? Or should I go ahead and get the substrings to obtain the qubits and populate the HasMaps from that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, that's not too difficult for any given PackedInstruction
object instr
you get while iterating over the circuit data you can call CircuitData::get_qargs(instr.qubits)
which will return a &[Qubit]
object. The Qubit
object is a newtype u32
. So you can do Qubit.0 as usize
to get the index into a vec for any Qubit
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, will follow that procedure. Thanks
# The stringified return type is because OrderedDict can't be subscripted before Python 3.9, and | ||
# typing.OrderedDict wasn't added until 3.7.2. It can be turned into a proper type once 3.6 | ||
# support is dropped. | ||
def count_ops_with_qubits(self) -> list: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The next qiskit release will only support python >= 3.9 so this isn't relevent. Also this isn't currently returning an ordered dict. Although I would argue we should not be using an ordered dict at all. The other methods do because they have been around since Qiskit supported much older versions of python (before dicts were sorted by default) and have retained it for backwards compatibility. But for new functionality we should just return a dict or a list.
# The stringified return type is because OrderedDict can't be subscripted before Python 3.9, and | |
# typing.OrderedDict wasn't added until 3.7.2. It can be turned into a proper type once 3.6 | |
# support is dropped. | |
def count_ops_with_qubits(self) -> list: |
pub fn operations_with_qubits(&self, py: Python<'_>) -> PyResult<Py<PyAny>> { | ||
let mut ret: Vec<(Option<String>, Option<String>)> = vec![]; | ||
for (_index, inst) in self.data.iter().enumerate() { | ||
let op_str = inst.op.view().name().to_string(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably try to avoid to_string()
here as this will make a copy of every string, and we can keep it all by reference in Rust space. When we return to Python that will do the copying for us.
for b in self.qargs_interner.get(inst.qubits) { | ||
ret.push(( | ||
Some(op_str.to_string()), | ||
Some(self.qubits.get(*b).unwrap().to_string()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we were going to use qubit objects like this, I'd have suggested returning them directly instead of stringify them. Then the python user could interact with the bit object directly. However, I don't think we need to work with actual bit objects and can just use indices. If a user wants to map the index to a bit, in python that's just circuit.qubits[index]
.
Pull Request Test Coverage Report for Build 10612643610Details
💛 - Coveralls |
fixes #12972
Summary
Added function to tally operations in circuit with associated qubits
fixes #12972
Details and comments
-Added function count_ops_with_qubits() in quantumcircuit.py, this returns the operations in the QuantumCircuit along with the qubits associated with those operations.
-The abovementioned function uses the Rust function fn operations_with_qubits in circuit_data.rs
-A test (test_count_ops_with_qubits()) has been added to file test/python/circuit/test_circuit_data.py
-tox has been run -- no test failures reported
-tox -eblack run to reformat files
[Yes ] I have added the tests to cover my changes.
[Yes ] I have updated the documentation accordingly.
[Yes ] I have read the CONTRIBUTING document.