Skip to content

Commit 35f9e7c

Browse files
authored
Remove cyclic-definition handling from Clifford.from_circuit (Qiskit#10441)
* Remove cyclic-definition handling from `Clifford.from_circuit` This reliance on `RecursionError` could lead to CPython crashing, when the user had raised the recursion limit beyond what their operating system could actually support in terms of Python frames. This was particularly an issue with Windows when in a context that `jedi` is active (such as in an IPython session, or if `seaborn` was imported), since `jedi` unilaterally sets the recursion limit to 3000, while CPython tends to overflow the stack on Windows at around 2700 frames. Recursive `definition` fields are not valid data in the Qiskit model, as the definition is supposed to be hierarchical, and a decomposition in terms of gates that do not involve the current one in any form. For defining equivalences that may involve cycles, one should use the `EquivalenceLibrary` objects that Terra manages, and the transpiler takes advantage of via the `BasisTranslator`. * Remove unused import
1 parent b831bcf commit 35f9e7c

File tree

3 files changed

+18
-49
lines changed

3 files changed

+18
-49
lines changed

qiskit/quantum_info/operators/symplectic/clifford_circuits.py

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,23 @@
1212
"""
1313
Circuit simulation for the Clifford class.
1414
"""
15+
1516
from __future__ import annotations
16-
import copy
17+
1718
import numpy as np
1819

1920
from qiskit.circuit import Barrier, Delay, Gate
2021
from qiskit.circuit.exceptions import CircuitError
2122
from qiskit.exceptions import QiskitError
2223

2324

24-
def _append_circuit(clifford, circuit, qargs=None, recursion_depth=0):
25+
def _append_circuit(clifford, circuit, qargs=None):
2526
"""Update Clifford inplace by applying a Clifford circuit.
2627
2728
Args:
2829
clifford (Clifford): The Clifford to update.
2930
circuit (QuantumCircuit): The circuit to apply.
3031
qargs (list or None): The qubits to apply circuit to.
31-
recursion_depth (int): The depth of mutual recursion with _append_operation
3232
3333
Returns:
3434
Clifford: the updated Clifford.
@@ -46,18 +46,17 @@ def _append_circuit(clifford, circuit, qargs=None, recursion_depth=0):
4646
)
4747
# Get the integer position of the flat register
4848
new_qubits = [qargs[circuit.find_bit(bit).index] for bit in instruction.qubits]
49-
clifford = _append_operation(clifford, instruction.operation, new_qubits, recursion_depth)
49+
clifford = _append_operation(clifford, instruction.operation, new_qubits)
5050
return clifford
5151

5252

53-
def _append_operation(clifford, operation, qargs=None, recursion_depth=0):
53+
def _append_operation(clifford, operation, qargs=None):
5454
"""Update Clifford inplace by applying a Clifford operation.
5555
5656
Args:
5757
clifford (Clifford): The Clifford to update.
5858
operation (Instruction or Clifford or str): The operation or composite operation to apply.
5959
qargs (list or None): The qubits to apply operation to.
60-
recursion_depth (int): The depth of mutual recursion with _append_circuit
6160
6261
Returns:
6362
Clifford: the updated Clifford.
@@ -135,16 +134,10 @@ def _append_operation(clifford, operation, qargs=None, recursion_depth=0):
135134
# This succeeds only if the gate has all-Clifford definition (decomposition).
136135
# If fails, we need to restore the clifford that was before attempting to unroll and append.
137136
if gate.definition is not None:
138-
if recursion_depth > 0:
139-
return _append_circuit(clifford, gate.definition, qargs, recursion_depth + 1)
140-
else: # recursion_depth == 0
141-
# clifford may be updated in _append_circuit
142-
org_clifford = copy.deepcopy(clifford)
143-
try:
144-
return _append_circuit(clifford, gate.definition, qargs, 1)
145-
except (QiskitError, RecursionError):
146-
# discard incompletely updated clifford and continue
147-
clifford = org_clifford
137+
try:
138+
return _append_circuit(clifford.copy(), gate.definition, qargs)
139+
except QiskitError:
140+
pass
148141

149142
# As a final attempt, if the gate is up to 3 qubits,
150143
# we try to construct a Clifford to be appended from its matrix representation.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
upgrade:
3+
- |
4+
:meth:`.Clifford.from_circuit` will no longer attempt to resolve instructions whose
5+
:attr:`~.circuit.Instruction.definition` fields are mutually recursive with some other object.
6+
Such recursive definitions are already a violation of the strictly hierarchical ordering that
7+
the :attr:`~.circuit.Instruction.definition` field requires, and code should not rely on this
8+
being possible at all. If you want to define equivalences that are permitted to have (mutual)
9+
cycles, use an :class:`.EquivalenceLibrary`.

test/python/quantum_info/operators/symplectic/test_clifford.py

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -555,39 +555,6 @@ def test_from_circuit_with_all_types(self):
555555
expected_clifford = Clifford.from_dict(expected_clifford_dict)
556556
self.assertEqual(combined_clifford, expected_clifford)
557557

558-
def test_from_gate_with_cyclic_definition(self):
559-
"""Test if a Clifford can be created from gate with cyclic definition"""
560-
561-
class MyHGate(HGate):
562-
"""Custom HGate class for test"""
563-
564-
def __init__(self):
565-
super().__init__()
566-
self.name = "my_h"
567-
568-
def _define(self):
569-
qc = QuantumCircuit(1, name=self.name)
570-
qc.s(0)
571-
qc.append(MySXGate(), [0])
572-
qc.s(0)
573-
self.definition = qc
574-
575-
class MySXGate(SXGate):
576-
"""Custom SXGate class for test"""
577-
578-
def __init__(self):
579-
super().__init__()
580-
self.name = "my_sx"
581-
582-
def _define(self):
583-
qc = QuantumCircuit(1, name=self.name)
584-
qc.sdg(0)
585-
qc.append(MyHGate(), [0])
586-
qc.sdg(0)
587-
self.definition = qc
588-
589-
Clifford(MyHGate())
590-
591558

592559
@ddt
593560
class TestCliffordSynthesis(QiskitTestCase):

0 commit comments

Comments
 (0)