Closed ryanhill1 closed 2 years ago
Update: The issue seems to persist for any circuit with an empty/unused register indexed within the range of the maximally indexed register. For example,
calculate_unitary
works:
T : |0| T : |0| T : |0|
q0 : -X- q0 : -X- q0 : -X-
q1 : -Y- q1 : -Y- T : |0|
q2 : -Z- T : |0|
T : |0|
- Circuits for which `calculate_unitary` *does not* work
```python3
T : |0| T : |0| T : |0| T : |0|
q1 : -Y- q0 : -X- q1 : -Y- q2 : -Z-
q2 : -Z- q2 : -Z- T : |0| T : |0|
T : |0| T : |0|
Hi @ryanhill1,
Thank you very much for reaching out!
We're aware of this limitation and our documentation states "When constructing a circuit with the simulators, Amazon Braket currently requires that you use contiguous qubits/indices."
Could you please tell us more about why this functionality (having empty/unused register index) is important to your use case?
@krneta thank you for your quick response! I'm sorry I wasn't already aware of this limitation. I stumbled upon it while working on the unitaryfund/mitiq error mitigation toolkit. Their project interfaces between a few different front-end circuit-building packages. They verify each circuit conversion by comparing the circuit unitary before and after conversion, but for some packages, don't require qubit equality. I was investigating why that was, and now it makes sense! This functionality isn't overly important to me, but I'm curious, does it have to do with a limitation of the backends themselves? Or is it to do with how the circuits are currently processed through the SDK?
My pleasure.
It's just a simplification we made early on that we haven't had time to go back to. That's why I was curious about your use case - to see if we need to look into doing that work again.
Gotcha. If you did decide to add support for circuits with non-contiguous qubits/indices, how do you think you would handle the unused registers? Suppose you started with the following circuit:
T : |0|1|2|
q0 : -H-C---
|
q2 : ---X-C-
|
q4 : -----X-
T : |0|1|2|
There seem to be two main approaches to handling the unused q1
and q3
registers in this example.
T : |0|1|2|
q0 : -H-C---
|
q1 : ---X-C-
|
q2 : -----X-
T : |0|1|2|
T : |0|1|2|
q0 : -H-C--- | q1 : -I- | --- |
---|
q2 : ---X-C- | q3 : -I--- | - |
---|
q4 : -----X-
T : |0|1|2|
Personally I would prefer to go with the first option (mostly because of performance implications), but I'd want to discuss it with a few more people on the team before we'd actually pick it up.
Sounds good!
Hi @ryanhill1,
You can use the big-endian calculate_unitary_big_endian
method:
import numpy as np
from braket.circuits import Circuit, Instruction, gates
from braket.circuits.unitary_calculation import calculate_unitary
qubit_index = 1
circuit = Circuit()
pgates = [
gates.Rx,
gates.Ry,
gates.Rz,
gates.PhaseShift,
]
angles = np.random.RandomState(11).random(len(pgates))
instructions = [
Instruction(rot(a), target=qubit_index)
for rot, a in zip(pgates, angles)
]
for instr in instructions:
circuit.add_instruction(instr)
# Alternatively, and more idiomatically,
# unitary = circuit.to_unitary()
unitary = calculate_unitary_big_endian(circuit.instructions, circuit.qubits)
Is there anything else that you need for this issue?
@speller26 Nothing else! Seems like the issue was fixed with Release v1.19.0
Describe the bug Calling
calculate_unitary
on a one-qubit circuit where the register is notq0
raises an error.To reproduce
The above works fine for
qubit_index=0
but raises anIndexError
for anyqubit_index!=0
Expected behavior The
calculate_unitary
function should account for the empty/unused registers and calculate the unitary of the circuit.Screenshots or logs
System information