Skip to content

Commit

Permalink
Fix creation of registers in synthesis methods (#13086)
Browse files Browse the repository at this point in the history
* add qregs to output circuits

* never develop while not on up-to-date main

* one shan't commit faster than ones IDE can run black

* avoid compose

(cherry picked from commit 1962704)

# Conflicts:
#	qiskit/synthesis/linear_phase/cz_depth_lnn.py
#	qiskit/synthesis/permutation/permutation_reverse_lnn.py
  • Loading branch information
Cryoris authored and mergify[bot] committed Sep 10, 2024
1 parent 91c566e commit 160868e
Show file tree
Hide file tree
Showing 12 changed files with 89 additions and 23 deletions.
53 changes: 39 additions & 14 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1152,17 +1152,41 @@ def __init__(
"""The unit that :attr:`duration` is specified in."""
self.metadata = {} if metadata is None else metadata
"""Arbitrary user-defined metadata for the circuit.
Qiskit will not examine the content of this mapping, but it will pass it through the
transpiler and reattach it to the output, so you can track your own metadata."""

@classmethod
def _from_circuit_data(cls, data: CircuitData) -> typing.Self:
def _from_circuit_data(cls, data: CircuitData, add_regs: bool = False) -> typing.Self:
"""A private constructor from rust space circuit data."""
out = QuantumCircuit()

if data.num_qubits > 0:
if add_regs:
qr = QuantumRegister(name="q", bits=data.qubits)
out.qregs = [qr]
out._qubit_indices = {
bit: BitLocations(index, [(qr, index)]) for index, bit in enumerate(data.qubits)
}
else:
out._qubit_indices = {
bit: BitLocations(index, []) for index, bit in enumerate(data.qubits)
}

if data.num_clbits > 0:
if add_regs:
cr = ClassicalRegister(name="c", bits=data.clbits)
out.cregs = [cr]
out._clbit_indices = {
bit: BitLocations(index, [(cr, index)]) for index, bit in enumerate(data.clbits)
}
else:
out._clbit_indices = {
bit: BitLocations(index, []) for index, bit in enumerate(data.clbits)
}

out._data = data
out._qubit_indices = {bit: BitLocations(index, []) for index, bit in enumerate(data.qubits)}
out._clbit_indices = {bit: BitLocations(index, []) for index, bit in enumerate(data.clbits)}

return out

@staticmethod
Expand Down Expand Up @@ -3013,16 +3037,7 @@ def add_register(self, *regs: Register | int | Sequence[Bit]) -> None:
self._ancillas.append(bit)

if isinstance(register, QuantumRegister):
self.qregs.append(register)

for idx, bit in enumerate(register):
if bit in self._qubit_indices:
self._qubit_indices[bit].registers.append((register, idx))
else:
self._data.add_qubit(bit)
self._qubit_indices[bit] = BitLocations(
self._data.num_qubits - 1, [(register, idx)]
)
self._add_qreg(register)

elif isinstance(register, ClassicalRegister):
self.cregs.append(register)
Expand All @@ -3041,6 +3056,16 @@ def add_register(self, *regs: Register | int | Sequence[Bit]) -> None:
else:
raise CircuitError("expected a register")

def _add_qreg(self, qreg: QuantumRegister) -> None:
self.qregs.append(qreg)

for idx, bit in enumerate(qreg):
if bit in self._qubit_indices:
self._qubit_indices[bit].registers.append((qreg, idx))
else:
self._data.add_qubit(bit)
self._qubit_indices[bit] = BitLocations(self._data.num_qubits - 1, [(qreg, idx)])

def add_bits(self, bits: Iterable[Bit]) -> None:
"""Add Bits to the circuit."""
duplicate_bits = {
Expand Down
2 changes: 1 addition & 1 deletion qiskit/synthesis/clifford/clifford_decompose_bm.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def synth_clifford_bm(clifford: Clifford) -> QuantumCircuit:
`arXiv:2003.09412 [quant-ph] <https://arxiv.org/abs/2003.09412>`_
"""
circuit = QuantumCircuit._from_circuit_data(
synth_clifford_bm_inner(clifford.tableau.astype(bool))
synth_clifford_bm_inner(clifford.tableau.astype(bool)), add_regs=True
)
circuit.name = str(clifford)
return circuit
2 changes: 1 addition & 1 deletion qiskit/synthesis/clifford/clifford_decompose_greedy.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def synth_clifford_greedy(clifford: Clifford) -> QuantumCircuit:
`arXiv:2105.02291 [quant-ph] <https://arxiv.org/abs/2105.02291>`_
"""
circuit = QuantumCircuit._from_circuit_data(
synth_clifford_greedy_inner(clifford.tableau.astype(bool))
synth_clifford_greedy_inner(clifford.tableau.astype(bool)), add_regs=True
)
circuit.name = str(clifford)
return circuit
2 changes: 1 addition & 1 deletion qiskit/synthesis/linear/cnot_synth.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,4 @@ def synth_cnot_count_full_pmh(
circuit_data = fast_pmh(normalized, section_size)

# construct circuit from the data
return QuantumCircuit._from_circuit_data(circuit_data)
return QuantumCircuit._from_circuit_data(circuit_data, add_regs=True)
7 changes: 7 additions & 0 deletions qiskit/synthesis/linear_phase/cz_depth_lnn.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ def synth_cz_depth_line_mr(mat: np.ndarray) -> QuantumCircuit:
# s_gates[i] = 0, 1, 2 or 3 for a gate id, sdg, z or s on qubit i respectively
s_gates = np.zeros(num_qubits)

<<<<<<< HEAD
qc = QuantumCircuit(num_qubits)
for i in range(num_qubits):
for j in range(i + 1, num_qubits):
Expand Down Expand Up @@ -192,3 +193,9 @@ def synth_cz_depth_line_mr(mat: np.ndarray) -> QuantumCircuit:
qc = _append_cx_stage1(qc, num_qubits)

return qc
=======
# Call Rust implementaton
return QuantumCircuit._from_circuit_data(
synth_cz_depth_line_mr_inner(mat.astype(bool)), add_regs=True
)
>>>>>>> 1962704cf (Fix creation of registers in synthesis methods (#13086))
3 changes: 2 additions & 1 deletion qiskit/synthesis/one_qubit/one_qubit_decompose.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,8 @@ def _decompose(self, unitary, simplify=True, atol=DEFAULT_ATOL):
return QuantumCircuit._from_circuit_data(
euler_one_qubit_decomposer.unitary_to_circuit(
unitary, [self.basis], 0, None, simplify, atol
)
),
add_regs=True,
)

@property
Expand Down
4 changes: 2 additions & 2 deletions qiskit/synthesis/permutation/permutation_full.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def synth_permutation_basic(pattern: list[int] | np.ndarray[int]) -> QuantumCirc
Returns:
The synthesized quantum circuit.
"""
return QuantumCircuit._from_circuit_data(_synth_permutation_basic(pattern))
return QuantumCircuit._from_circuit_data(_synth_permutation_basic(pattern), add_regs=True)


def synth_permutation_acg(pattern: list[int] | np.ndarray[int]) -> QuantumCircuit:
Expand Down Expand Up @@ -75,4 +75,4 @@ def synth_permutation_acg(pattern: list[int] | np.ndarray[int]) -> QuantumCircui
*Routing Permutations on Graphs Via Matchings.*,
`(Full paper) <https://www.cs.tau.ac.il/~nogaa/PDFS/r.pdf>`_
"""
return QuantumCircuit._from_circuit_data(_synth_permutation_acg(pattern))
return QuantumCircuit._from_circuit_data(_synth_permutation_acg(pattern), add_regs=True)
4 changes: 3 additions & 1 deletion qiskit/synthesis/permutation/permutation_lnn.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,6 @@ def synth_permutation_depth_lnn_kms(pattern: list[int] | np.ndarray[int]) -> Qua
# In the permutation synthesis code below the notation is opposite:
# [2, 4, 3, 0, 1] means that 0 maps to 2, 1 to 3, 2 to 3, 3 to 0, and 4 to 1.
# This is why we invert the pattern.
return QuantumCircuit._from_circuit_data(_synth_permutation_depth_lnn_kms(pattern))
return QuantumCircuit._from_circuit_data(
_synth_permutation_depth_lnn_kms(pattern), add_regs=True
)
7 changes: 7 additions & 0 deletions qiskit/synthesis/permutation/permutation_reverse_lnn.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,14 @@ def synth_permutation_reverse_lnn_kms(num_qubits: int) -> QuantumCircuit:
`arXiv:quant-ph/0701194 <https://arxiv.org/abs/quant-ph/0701194>`_
"""

<<<<<<< HEAD
qc = QuantumCircuit(num_qubits)
_append_reverse_permutation_lnn_kms(qc, num_qubits)

return qc
=======
# Call Rust implementation
return QuantumCircuit._from_circuit_data(
synth_permutation_reverse_lnn_kms_inner(num_qubits), add_regs=True
)
>>>>>>> 1962704cf (Fix creation of registers in synthesis methods (#13086))
4 changes: 2 additions & 2 deletions qiskit/synthesis/two_qubit/two_qubit_decompose.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ def circuit(
circuit_data = self._inner_decomposition.circuit(
euler_basis=euler_basis, simplify=simplify, atol=atol
)
return QuantumCircuit._from_circuit_data(circuit_data)
return QuantumCircuit._from_circuit_data(circuit_data, add_regs=True)

def actual_fidelity(self, **kwargs) -> float:
"""Calculates the actual fidelity of the decomposed circuit to the input unitary."""
Expand Down Expand Up @@ -671,7 +671,7 @@ def __call__(
approximate,
_num_basis_uses=_num_basis_uses,
)
return QuantumCircuit._from_circuit_data(circ_data)
return QuantumCircuit._from_circuit_data(circ_data, add_regs=True)
else:
sequence = self._inner_decomposer(
np.asarray(unitary, dtype=complex),
Expand Down
7 changes: 7 additions & 0 deletions releasenotes/notes/fix-synth-qregs-7662681c0ff02511.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
fixes:
- |
Fixed a bug where various synthesis methods created circuits without quantum or
classical registers. This also affected functions that internally used the synthesis
methods, such as :meth:`.Clifford.to_circuit`.
Fixed `#13041 <https://github.com/Qiskit/qiskit/issues/13041>`__.
17 changes: 17 additions & 0 deletions test/python/quantum_info/operators/symplectic/test_clifford.py
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,23 @@ def test_to_circuit(self, num_qubits):
# Convert back to clifford and check it is the same
self.assertEqual(Clifford(decomp), target)

def test_to_circuit_manual(self):
"""Test a manual comparison to a known circuit.
This also tests whether the resulting Clifford circuit has quantum registers, thereby
regression testing #13041.
"""
# this is set to a circuit that remains the same under Clifford reconstruction
circuit = QuantumCircuit(2)
circuit.z(0)
circuit.h(0)
circuit.cx(0, 1)

cliff = Clifford(circuit)
reconstructed = cliff.to_circuit()

self.assertEqual(circuit, reconstructed)

@combine(num_qubits=[1, 2, 3, 4, 5])
def test_to_instruction(self, num_qubits):
"""Test to_instruction method"""
Expand Down

0 comments on commit 160868e

Please sign in to comment.