From ebd0a731df322a4aeacda244ba11ffea74cccdf1 Mon Sep 17 00:00:00 2001 From: RasmitDevkota Date: Sun, 24 Aug 2025 18:17:26 -0400 Subject: [PATCH 01/14] implemented support for Var, Unary, and Binary operations during qiskit_to_tk conversion --- pytket/extensions/qiskit/qiskit_convert.py | 91 ++++++++++++++++++++-- 1 file changed, 83 insertions(+), 8 deletions(-) diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index 9c6e56b7..1fc972a9 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -91,6 +91,11 @@ StatePreparation, UnitaryGate, ) +from qiskit.circuit.classical.expr import ( + Unary, + Binary, + Var, +) if TYPE_CHECKING: from qiskit_ibm_runtime.ibm_backend import IBMBackend # type: ignore @@ -677,14 +682,84 @@ def _append_if_else_circuit( if_circ, else_circ = _pytket_circuits_from_ifelseop( if_else_op, outer_builder, qargs, cargs ) + + def flatten_condition(_condition): + if isinstance(_condition, Var): + for bit in bits: + if bit.reg_name == _condition.var._register.name and bit.index[0] == _condition.var._index: + return bit + elif isinstance(_condition, Unary): + val = 0 if _condition.op.name == "BIT_NOT" else 1 + for bit in bits: + if bit.reg_name == _condition.operand.var._register.name and bit.index[0] == _condition.operand.var._index: + return bit ^ val + elif isinstance(_condition, Binary): + if _condition.op.name == "BIT_AND": + return flatten_condition(_condition.left) & flatten_condition(_condition.right) + elif _condition.op.name == "BIT_OR": + return flatten_condition(_condition.left) | flatten_condition(_condition.right) + elif _condition.op.name == "BIT_XOR": + return flatten_condition(_condition.left) ^ flatten_condition(_condition.right) + else: + raise ValueError() + else: + raise TypeError() + # else_circ can be None if no false_body is specified. - if isinstance(if_else_op.condition[0], Clbit): - if len(bits) != 1: - raise NotImplementedError("Conditions on multiple bits not supported") + if isinstance(if_else_op.condition, (Var, Unary, Binary)): + # In this case, if_else_op.condition is a Binary operation which we must flatten + condition_flattened = flatten_condition(if_else_op.condition) + + outer_builder.tkc.add_circbox( + circbox=CircBox(if_circ), + args=if_circ.qubits + if_circ.bits, # type: ignore + condition=condition_flattened, + ) + # If we have an else_circ defined, add it to the circuit + if else_circ is not None: + outer_builder.tkc.add_circbox( + circbox=CircBox(else_circ), + args=else_circ.qubits + else_circ.bits, # type: ignore + condition=1 ^ condition_flattened, + ) + elif isinstance(if_else_op.condition, Var) and isinstance(if_else_op.condition.var, Clbit): + condition_bits = [] + for bit in bits: + if bit.reg_name == if_else_op.condition.var._register.name and bit.index[0] == if_else_op.condition.var._index: + condition_bits.append(bit) + + if len(condition_bits) == 0: + raise ValueError(f"Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp.") + + # In this case, if_else_op.condition is a single tuple of shape (Clbit, value) + outer_builder.tkc.add_circbox( + circbox=CircBox(if_circ), + args=if_circ.qubits + if_circ.bits, # type: ignore + condition_bits=condition_bits, + condition_value=1, + ) + # If we have an else_circ defined, add it to the circuit + if else_circ is not None: + outer_builder.tkc.add_circbox( + circbox=CircBox(else_circ), + args=else_circ.qubits + else_circ.bits, # type: ignore + condition_bits=condition_bits, + condition_value=0, + ) + elif hasattr(if_else_op.condition, "__getitem__") and isinstance(if_else_op.condition[0], Clbit): + condition_bits = [] + for bit in bits: + if bit.reg_name == if_else_op.condition[0]._register.name and bit.index[0] == if_else_op.condition[0]._index: + condition_bits.append(bit) + + if len(condition_bits) == 0: + raise ValueError(f"Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp.") + + # In this case, if_else_op.condition is a single tuple of shape (Clbit, value) outer_builder.tkc.add_circbox( circbox=CircBox(if_circ), args=if_circ.qubits + if_circ.bits, # type: ignore - condition_bits=bits, + condition_bits=condition_bits, condition_value=if_else_op.condition[1], ) # If we have an else_circ defined, add it to the circuit @@ -692,14 +767,14 @@ def _append_if_else_circuit( outer_builder.tkc.add_circbox( circbox=CircBox(else_circ), args=else_circ.qubits + else_circ.bits, # type: ignore - condition_bits=bits, + condition_bits=condition_bits, condition_value=1 ^ if_else_op.condition[1], ) - - elif isinstance(if_else_op.condition[0], ClassicalRegister): + elif hasattr(if_else_op.condition, "__getitem__") and isinstance(if_else_op.condition[0], ClassicalRegister): pytket_bit_reg: BitRegister = outer_builder.tkc.get_c_register( if_else_op.condition[0].name ) + outer_builder.tkc.add_circbox( circbox=CircBox(if_circ), args=if_circ.qubits + if_circ.bits, # type: ignore @@ -714,7 +789,7 @@ def _append_if_else_circuit( else: raise TypeError( "Unrecognized type used to construct IfElseOp. Expected " # noqa: ISC003 - + f"ClBit or ClassicalRegister, got {type(if_else_op.condition[0])}" + + f"Var, Unary, Binary, or ClBit or ClassicalRegister tuple, got {type(if_else_op.condition)}" ) From 6fa0f6f8db860ce900f5e97e5d144e932c574b82 Mon Sep 17 00:00:00 2001 From: RasmitDevkota Date: Sun, 24 Aug 2025 18:25:36 -0400 Subject: [PATCH 02/14] added comments describing code and filled in error messages --- pytket/extensions/qiskit/qiskit_convert.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index 1fc972a9..9f258652 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -683,17 +683,23 @@ def _append_if_else_circuit( if_else_op, outer_builder, qargs, cargs ) + # Utility method to flatten conditions def flatten_condition(_condition): if isinstance(_condition, Var): + # Find the pytket Bit with the same register name and index as the Qiskit Clbit for bit in bits: if bit.reg_name == _condition.var._register.name and bit.index[0] == _condition.var._index: return bit elif isinstance(_condition, Unary): + # Set the comparison value based on whether the Unary involves a NOT operation val = 0 if _condition.op.name == "BIT_NOT" else 1 + + # Find the pytket Bit with the same register name and index as the Qiskit Clbit for bit in bits: if bit.reg_name == _condition.operand.var._register.name and bit.index[0] == _condition.operand.var._index: return bit ^ val elif isinstance(_condition, Binary): + # Recursively handle both operands of the binary operation if _condition.op.name == "BIT_AND": return flatten_condition(_condition.left) & flatten_condition(_condition.right) elif _condition.op.name == "BIT_OR": @@ -701,9 +707,9 @@ def flatten_condition(_condition): elif _condition.op.name == "BIT_XOR": return flatten_condition(_condition.left) ^ flatten_condition(_condition.right) else: - raise ValueError() + raise NotImplementedError(f"Binary condition with operation '{_condition.op.name}' not supported") else: - raise TypeError() + raise NotImplementedError(f"Condition of type {type(_condition)} not supported") # else_circ can be None if no false_body is specified. if isinstance(if_else_op.condition, (Var, Unary, Binary)): From 3f41b3550bc93fd0c7ddf580d0c5d7f764a6544f Mon Sep 17 00:00:00 2001 From: RasmitDevkota Date: Tue, 26 Aug 2025 11:12:46 -0400 Subject: [PATCH 03/14] fixed linting errors --- pytket/extensions/qiskit/qiskit_convert.py | 32 ++++++++++------------ 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index 9f258652..ca8556c6 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -92,8 +92,8 @@ UnitaryGate, ) from qiskit.circuit.classical.expr import ( - Unary, Binary, + Unary, Var, ) @@ -688,31 +688,35 @@ def flatten_condition(_condition): if isinstance(_condition, Var): # Find the pytket Bit with the same register name and index as the Qiskit Clbit for bit in bits: - if bit.reg_name == _condition.var._register.name and bit.index[0] == _condition.var._index: + if bit.reg_name == _condition.var._register.name and bit.index[0] == _condition.var._index: # noqa: SLF001 return bit + + raise ValueError("Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp.") elif isinstance(_condition, Unary): # Set the comparison value based on whether the Unary involves a NOT operation val = 0 if _condition.op.name == "BIT_NOT" else 1 # Find the pytket Bit with the same register name and index as the Qiskit Clbit for bit in bits: - if bit.reg_name == _condition.operand.var._register.name and bit.index[0] == _condition.operand.var._index: + if bit.reg_name == _condition.operand.var._register.name and bit.index[0] == _condition.operand.var._index: # noqa: SLF001 return bit ^ val + + raise ValueError("Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp.") elif isinstance(_condition, Binary): # Recursively handle both operands of the binary operation if _condition.op.name == "BIT_AND": return flatten_condition(_condition.left) & flatten_condition(_condition.right) - elif _condition.op.name == "BIT_OR": + if _condition.op.name == "BIT_OR": return flatten_condition(_condition.left) | flatten_condition(_condition.right) - elif _condition.op.name == "BIT_XOR": + if _condition.op.name == "BIT_XOR": return flatten_condition(_condition.left) ^ flatten_condition(_condition.right) - else: - raise NotImplementedError(f"Binary condition with operation '{_condition.op.name}' not supported") + + raise NotImplementedError(f"Binary condition with operation '{_condition.op.name}' not supported") else: raise NotImplementedError(f"Condition of type {type(_condition)} not supported") # else_circ can be None if no false_body is specified. - if isinstance(if_else_op.condition, (Var, Unary, Binary)): + if isinstance(if_else_op.condition, (Var | Unary | Binary)): # In this case, if_else_op.condition is a Binary operation which we must flatten condition_flattened = flatten_condition(if_else_op.condition) @@ -729,13 +733,10 @@ def flatten_condition(_condition): condition=1 ^ condition_flattened, ) elif isinstance(if_else_op.condition, Var) and isinstance(if_else_op.condition.var, Clbit): - condition_bits = [] - for bit in bits: - if bit.reg_name == if_else_op.condition.var._register.name and bit.index[0] == if_else_op.condition.var._index: - condition_bits.append(bit) + condition_bits = [bit for bit in bits if bit.reg_name == if_else_op.condition.var._register.name and bit.index[0] == if_else_op.condition.var._index] # noqa: SLF001 if len(condition_bits) == 0: - raise ValueError(f"Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp.") + raise ValueError("Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp.") # In this case, if_else_op.condition is a single tuple of shape (Clbit, value) outer_builder.tkc.add_circbox( @@ -753,10 +754,7 @@ def flatten_condition(_condition): condition_value=0, ) elif hasattr(if_else_op.condition, "__getitem__") and isinstance(if_else_op.condition[0], Clbit): - condition_bits = [] - for bit in bits: - if bit.reg_name == if_else_op.condition[0]._register.name and bit.index[0] == if_else_op.condition[0]._index: - condition_bits.append(bit) + condition_bits = [bit for bit in bits if bit.reg_name == if_else_op.condition.var._register.name and bit.index[0] == if_else_op.condition.var._index] # noqa: SLF001 if len(condition_bits) == 0: raise ValueError(f"Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp.") From b979b694631b8558d9e2d8f2dbefb613e3fe2dc4 Mon Sep 17 00:00:00 2001 From: RasmitDevkota Date: Tue, 26 Aug 2025 11:40:02 -0400 Subject: [PATCH 04/14] fixed mismatched bit access lines --- pytket/extensions/qiskit/qiskit_convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index ca8556c6..e3f606db 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -754,7 +754,7 @@ def flatten_condition(_condition): condition_value=0, ) elif hasattr(if_else_op.condition, "__getitem__") and isinstance(if_else_op.condition[0], Clbit): - condition_bits = [bit for bit in bits if bit.reg_name == if_else_op.condition.var._register.name and bit.index[0] == if_else_op.condition.var._index] # noqa: SLF001 + condition_bits = [bit for bit in bits if bit.reg_name == if_else_op.condition[0]._register.name and bit.index[0] == if_else_op.condition[0]._index] # noqa: SLF001 if len(condition_bits) == 0: raise ValueError(f"Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp.") From 726626285679dc7b8d3ccff9ac8df456f0008d39 Mon Sep 17 00:00:00 2001 From: RasmitDevkota Date: Tue, 26 Aug 2025 11:49:37 -0400 Subject: [PATCH 05/14] added types to flatten_conditions signature --- pytket/extensions/qiskit/qiskit_convert.py | 25 +++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index e3f606db..5255e5d6 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -55,7 +55,7 @@ Unitary3qBox, UnitType, ) -from pytket.circuit.logic_exp import reg_eq, reg_neq +from pytket.circuit.logic_exp import BitLogicExp, reg_eq, reg_neq from pytket.passes import AutoRebase from pytket.pauli import Pauli, QubitPauliString from pytket.unit_id import _TEMP_BIT_NAME @@ -83,6 +83,11 @@ Reset, ) from qiskit.circuit import Qubit as QCQubit +from qiskit.circuit.classical.expr import ( + Binary, + Unary, + Var, +) from qiskit.circuit.library import ( CRYGate, Initialize, @@ -91,11 +96,6 @@ StatePreparation, UnitaryGate, ) -from qiskit.circuit.classical.expr import ( - Binary, - Unary, - Var, -) if TYPE_CHECKING: from qiskit_ibm_runtime.ibm_backend import IBMBackend # type: ignore @@ -684,7 +684,9 @@ def _append_if_else_circuit( ) # Utility method to flatten conditions - def flatten_condition(_condition): + def flatten_condition( + _condition: Var | Unary | Binary + ) -> BitLogicExp: if isinstance(_condition, Var): # Find the pytket Bit with the same register name and index as the Qiskit Clbit for bit in bits: @@ -692,7 +694,7 @@ def flatten_condition(_condition): return bit raise ValueError("Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp.") - elif isinstance(_condition, Unary): + if isinstance(_condition, Unary): # Set the comparison value based on whether the Unary involves a NOT operation val = 0 if _condition.op.name == "BIT_NOT" else 1 @@ -702,7 +704,7 @@ def flatten_condition(_condition): return bit ^ val raise ValueError("Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp.") - elif isinstance(_condition, Binary): + if isinstance(_condition, Binary): # Recursively handle both operands of the binary operation if _condition.op.name == "BIT_AND": return flatten_condition(_condition.left) & flatten_condition(_condition.right) @@ -712,8 +714,7 @@ def flatten_condition(_condition): return flatten_condition(_condition.left) ^ flatten_condition(_condition.right) raise NotImplementedError(f"Binary condition with operation '{_condition.op.name}' not supported") - else: - raise NotImplementedError(f"Condition of type {type(_condition)} not supported") + raise NotImplementedError(f"Condition of type {type(_condition)} not supported") # else_circ can be None if no false_body is specified. if isinstance(if_else_op.condition, (Var | Unary | Binary)): @@ -757,7 +758,7 @@ def flatten_condition(_condition): condition_bits = [bit for bit in bits if bit.reg_name == if_else_op.condition[0]._register.name and bit.index[0] == if_else_op.condition[0]._index] # noqa: SLF001 if len(condition_bits) == 0: - raise ValueError(f"Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp.") + raise ValueError("Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp.") # In this case, if_else_op.condition is a single tuple of shape (Clbit, value) outer_builder.tkc.add_circbox( From 3d84c13f77816bcf8ceaaa05069b54fc8a9a6568 Mon Sep 17 00:00:00 2001 From: RasmitDevkota Date: Tue, 26 Aug 2025 12:06:33 -0400 Subject: [PATCH 06/14] applied extra ruff formatting --- pytket/extensions/qiskit/qiskit_convert.py | 72 ++++++++++++++++------ 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index 5255e5d6..4fcd4423 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -684,36 +684,52 @@ def _append_if_else_circuit( ) # Utility method to flatten conditions - def flatten_condition( - _condition: Var | Unary | Binary - ) -> BitLogicExp: + def flatten_condition(_condition: Var | Unary | Binary) -> BitLogicExp: if isinstance(_condition, Var): # Find the pytket Bit with the same register name and index as the Qiskit Clbit for bit in bits: - if bit.reg_name == _condition.var._register.name and bit.index[0] == _condition.var._index: # noqa: SLF001 + if ( + bit.reg_name == _condition.var._register.name + and bit.index[0] == _condition.var._index + ): # noqa: SLF001 return bit - raise ValueError("Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp.") + raise ValueError( + "Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp." + ) if isinstance(_condition, Unary): # Set the comparison value based on whether the Unary involves a NOT operation val = 0 if _condition.op.name == "BIT_NOT" else 1 # Find the pytket Bit with the same register name and index as the Qiskit Clbit for bit in bits: - if bit.reg_name == _condition.operand.var._register.name and bit.index[0] == _condition.operand.var._index: # noqa: SLF001 + if ( + bit.reg_name == _condition.operand.var._register.name + and bit.index[0] == _condition.operand.var._index + ): # noqa: SLF001 return bit ^ val - raise ValueError("Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp.") + raise ValueError( + "Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp." + ) if isinstance(_condition, Binary): # Recursively handle both operands of the binary operation if _condition.op.name == "BIT_AND": - return flatten_condition(_condition.left) & flatten_condition(_condition.right) + return flatten_condition(_condition.left) & flatten_condition( + _condition.right + ) if _condition.op.name == "BIT_OR": - return flatten_condition(_condition.left) | flatten_condition(_condition.right) + return flatten_condition(_condition.left) | flatten_condition( + _condition.right + ) if _condition.op.name == "BIT_XOR": - return flatten_condition(_condition.left) ^ flatten_condition(_condition.right) + return flatten_condition(_condition.left) ^ flatten_condition( + _condition.right + ) - raise NotImplementedError(f"Binary condition with operation '{_condition.op.name}' not supported") + raise NotImplementedError( + f"Binary condition with operation '{_condition.op.name}' not supported" + ) raise NotImplementedError(f"Condition of type {type(_condition)} not supported") # else_circ can be None if no false_body is specified. @@ -733,11 +749,20 @@ def flatten_condition( args=else_circ.qubits + else_circ.bits, # type: ignore condition=1 ^ condition_flattened, ) - elif isinstance(if_else_op.condition, Var) and isinstance(if_else_op.condition.var, Clbit): - condition_bits = [bit for bit in bits if bit.reg_name == if_else_op.condition.var._register.name and bit.index[0] == if_else_op.condition.var._index] # noqa: SLF001 + elif isinstance(if_else_op.condition, Var) and isinstance( + if_else_op.condition.var, Clbit + ): + condition_bits = [ + bit + for bit in bits + if bit.reg_name == if_else_op.condition.var._register.name + and bit.index[0] == if_else_op.condition.var._index + ] # noqa: SLF001 if len(condition_bits) == 0: - raise ValueError("Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp.") + raise ValueError( + "Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp." + ) # In this case, if_else_op.condition is a single tuple of shape (Clbit, value) outer_builder.tkc.add_circbox( @@ -754,11 +779,20 @@ def flatten_condition( condition_bits=condition_bits, condition_value=0, ) - elif hasattr(if_else_op.condition, "__getitem__") and isinstance(if_else_op.condition[0], Clbit): - condition_bits = [bit for bit in bits if bit.reg_name == if_else_op.condition[0]._register.name and bit.index[0] == if_else_op.condition[0]._index] # noqa: SLF001 + elif hasattr(if_else_op.condition, "__getitem__") and isinstance( + if_else_op.condition[0], Clbit + ): + condition_bits = [ + bit + for bit in bits + if bit.reg_name == if_else_op.condition[0]._register.name + and bit.index[0] == if_else_op.condition[0]._index + ] # noqa: SLF001 if len(condition_bits) == 0: - raise ValueError("Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp.") + raise ValueError( + "Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp." + ) # In this case, if_else_op.condition is a single tuple of shape (Clbit, value) outer_builder.tkc.add_circbox( @@ -775,7 +809,9 @@ def flatten_condition( condition_bits=condition_bits, condition_value=1 ^ if_else_op.condition[1], ) - elif hasattr(if_else_op.condition, "__getitem__") and isinstance(if_else_op.condition[0], ClassicalRegister): + elif hasattr(if_else_op.condition, "__getitem__") and isinstance( + if_else_op.condition[0], ClassicalRegister + ): pytket_bit_reg: BitRegister = outer_builder.tkc.get_c_register( if_else_op.condition[0].name ) From b81e3485c381f5fc35c0e34e48da68a0bcff1bb0 Mon Sep 17 00:00:00 2001 From: RasmitDevkota Date: Tue, 26 Aug 2025 18:47:14 -0400 Subject: [PATCH 07/14] moved noqa comments back to correct lines --- pytket/extensions/qiskit/qiskit_convert.py | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index 4fcd4423..0233b2f1 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -689,9 +689,9 @@ def flatten_condition(_condition: Var | Unary | Binary) -> BitLogicExp: # Find the pytket Bit with the same register name and index as the Qiskit Clbit for bit in bits: if ( - bit.reg_name == _condition.var._register.name - and bit.index[0] == _condition.var._index - ): # noqa: SLF001 + bit.reg_name == _condition.var._register.name # noqa: SLF001 + and bit.index[0] == _condition.var._index # noqa: SLF001 + ): return bit raise ValueError( @@ -704,9 +704,9 @@ def flatten_condition(_condition: Var | Unary | Binary) -> BitLogicExp: # Find the pytket Bit with the same register name and index as the Qiskit Clbit for bit in bits: if ( - bit.reg_name == _condition.operand.var._register.name - and bit.index[0] == _condition.operand.var._index - ): # noqa: SLF001 + bit.reg_name == _condition.operand.var._register.name # noqa: SLF001 + and bit.index[0] == _condition.operand.var._index # noqa: SLF001 + ): return bit ^ val raise ValueError( @@ -755,9 +755,9 @@ def flatten_condition(_condition: Var | Unary | Binary) -> BitLogicExp: condition_bits = [ bit for bit in bits - if bit.reg_name == if_else_op.condition.var._register.name - and bit.index[0] == if_else_op.condition.var._index - ] # noqa: SLF001 + if bit.reg_name == if_else_op.condition.var._register.name # noqa: SLF001 + and bit.index[0] == if_else_op.condition.var._index # noqa: SLF001 + ] if len(condition_bits) == 0: raise ValueError( @@ -785,9 +785,9 @@ def flatten_condition(_condition: Var | Unary | Binary) -> BitLogicExp: condition_bits = [ bit for bit in bits - if bit.reg_name == if_else_op.condition[0]._register.name - and bit.index[0] == if_else_op.condition[0]._index - ] # noqa: SLF001 + if bit.reg_name == if_else_op.condition[0]._register.name # noqa: SLF001 + and bit.index[0] == if_else_op.condition[0]._index # noqa: SLF001 + ] if len(condition_bits) == 0: raise ValueError( From 549c409a9f77d49582f9336495f1c9db5a516c53 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Wed, 24 Sep 2025 15:59:13 +0100 Subject: [PATCH 08/14] add test case which was failing before --- tests/qiskit_convert_test.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/qiskit_convert_test.py b/tests/qiskit_convert_test.py index b1f1b257..d79a37ae 100644 --- a/tests/qiskit_convert_test.py +++ b/tests/qiskit_convert_test.py @@ -86,7 +86,7 @@ RebaseTket, SequencePass, ) -from pytket.unit_id import _TEMP_BIT_NAME +from pytket.unit_id import _TEMP_BIT_NAME, BitRegister from pytket.utils.results import ( compare_statevectors, compare_unitaries, @@ -1495,6 +1495,28 @@ def test_qiskitv2_conversions() -> None: assert if_tk1.condition == (ClassicalRegister(2, "c"), 2) +def test_bit_ref_circuit() -> None: + qreg = QuantumRegister(1) + qreg_setter = QuantumRegister(2) + creg_A = ClassicalRegister(1) + creg_B = ClassicalRegister(1) + + qc = QuantumCircuit(qreg, qreg_setter, creg_A, creg_B) + qc.x(qreg_setter[1]) + + with qc.if_test((creg_A[0], 0)) as _else: + qc.measure(qreg_setter[1], creg_B[0]) + with _else: + qc.measure(qreg_setter[0], creg_B[0]) + tkc = qiskit_to_tk(qc) + cregs = tkc.c_registers + assert len(cregs) == 2 + a_creg: BitRegister = cregs[0] + b_creg: BitRegister = cregs[1] + assert a_creg.size == 1 + assert b_creg.size == 1 + + def test_round_trip_with_qiskit_transpilation() -> None: circ = Circuit(4, 1) circ.H(0).Measure(0, 0) From 66706fa079b5f0af4189323e775f134f4e7d1a0c Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:05:56 +0100 Subject: [PATCH 09/14] import fix --- tests/qiskit_convert_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qiskit_convert_test.py b/tests/qiskit_convert_test.py index 752b3c3b..b756c358 100644 --- a/tests/qiskit_convert_test.py +++ b/tests/qiskit_convert_test.py @@ -43,7 +43,7 @@ RebaseTket, SequencePass, ) -from pytket.unit_id import _TEMP_BIT_NAME +from pytket.unit_id import _TEMP_BIT_NAME, BitRegister from pytket.utils.results import ( compare_statevectors, compare_unitaries, From 10995c28558bffa7da0db6e3f6872249c58b5520 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Mon, 29 Sep 2025 09:18:56 +0100 Subject: [PATCH 10/14] refactor out _flatten_condition --- pytket/extensions/qiskit/qiskit_convert.py | 101 +++++++++++---------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index 223404c7..73b1868d 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -605,6 +605,58 @@ def _build_rename_map( return rename_map +# Utility method to flatten conditions +def flatten_condition( + _condition: Var | Unary | Binary, bits: list[Bit] +) -> BitLogicExp | Bit: + if isinstance(_condition, Var): + # Find the pytket Bit with the same register name and index as the Qiskit Clbit + for bit in bits: + if ( + bit.reg_name == _condition.var._register.name # noqa: SLF001 + and bit.index[0] == _condition.var._index # noqa: SLF001 + ): + return bit + + raise ValueError( + "Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp." + ) + if isinstance(_condition, Unary): + # Set the comparison value based on whether the Unary involves a NOT operation + val = 0 if _condition.op.name == "BIT_NOT" else 1 + + # Find the pytket Bit with the same register name and index as the Qiskit Clbit + for bit in bits: + if ( + bit.reg_name == _condition.operand.var._register.name # noqa: SLF001 + and bit.index[0] == _condition.operand.var._index # noqa: SLF001 + ): + return bit ^ val + + raise ValueError( + "Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp." + ) + if isinstance(_condition, Binary): + # Recursively handle both operands of the binary operation + if _condition.op.name == "BIT_AND": + return flatten_condition(_condition.left, bits) & flatten_condition( + _condition.right, bits + ) + if _condition.op.name == "BIT_OR": + return flatten_condition(_condition.left) | flatten_condition( + _condition.right, bits + ) + if _condition.op.name == "BIT_XOR": + return flatten_condition(_condition.left) ^ flatten_condition( + _condition.right, bits + ) + + raise NotImplementedError( + f"Binary condition with operation '{_condition.op.name}' not supported" + ) + raise NotImplementedError(f"Condition of type {type(_condition)} not supported") + + # Used for handling of IfElseOp # docs -> https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.IfElseOp # Examples -> https://docs.quantum.ibm.com/guides/classical-feedforward-and-control-flow @@ -685,55 +737,6 @@ def _append_if_else_circuit( if_else_op, outer_builder, qargs, cargs ) - # Utility method to flatten conditions - def flatten_condition(_condition: Var | Unary | Binary) -> BitLogicExp: - if isinstance(_condition, Var): - # Find the pytket Bit with the same register name and index as the Qiskit Clbit - for bit in bits: - if ( - bit.reg_name == _condition.var._register.name # noqa: SLF001 - and bit.index[0] == _condition.var._index # noqa: SLF001 - ): - return bit - - raise ValueError( - "Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp." - ) - if isinstance(_condition, Unary): - # Set the comparison value based on whether the Unary involves a NOT operation - val = 0 if _condition.op.name == "BIT_NOT" else 1 - - # Find the pytket Bit with the same register name and index as the Qiskit Clbit - for bit in bits: - if ( - bit.reg_name == _condition.operand.var._register.name # noqa: SLF001 - and bit.index[0] == _condition.operand.var._index # noqa: SLF001 - ): - return bit ^ val - - raise ValueError( - "Failed to find any pytket Bit matching Qiskit Clbit in condition for IfElseOp." - ) - if isinstance(_condition, Binary): - # Recursively handle both operands of the binary operation - if _condition.op.name == "BIT_AND": - return flatten_condition(_condition.left) & flatten_condition( - _condition.right - ) - if _condition.op.name == "BIT_OR": - return flatten_condition(_condition.left) | flatten_condition( - _condition.right - ) - if _condition.op.name == "BIT_XOR": - return flatten_condition(_condition.left) ^ flatten_condition( - _condition.right - ) - - raise NotImplementedError( - f"Binary condition with operation '{_condition.op.name}' not supported" - ) - raise NotImplementedError(f"Condition of type {type(_condition)} not supported") - # else_circ can be None if no false_body is specified. if isinstance(if_else_op.condition, (Var | Unary | Binary)): # In this case, if_else_op.condition is a Binary operation which we must flatten From f875ef0959c5c2d87143c6ee0ebbaae18fcf5845 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Tue, 30 Sep 2025 09:54:06 +0100 Subject: [PATCH 11/14] underscores --- pytket/extensions/qiskit/qiskit_convert.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index 73b1868d..242c1cc6 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -606,7 +606,7 @@ def _build_rename_map( # Utility method to flatten conditions -def flatten_condition( +def _flatten_condition( _condition: Var | Unary | Binary, bits: list[Bit] ) -> BitLogicExp | Bit: if isinstance(_condition, Var): @@ -639,15 +639,15 @@ def flatten_condition( if isinstance(_condition, Binary): # Recursively handle both operands of the binary operation if _condition.op.name == "BIT_AND": - return flatten_condition(_condition.left, bits) & flatten_condition( + return _flatten_condition(_condition.left, bits) & _flatten_condition( _condition.right, bits ) if _condition.op.name == "BIT_OR": - return flatten_condition(_condition.left) | flatten_condition( + return _flatten_condition(_condition.left) | _flatten_condition( _condition.right, bits ) if _condition.op.name == "BIT_XOR": - return flatten_condition(_condition.left) ^ flatten_condition( + return _flatten_condition(_condition.left) ^ _flatten_condition( _condition.right, bits ) @@ -740,7 +740,7 @@ def _append_if_else_circuit( # else_circ can be None if no false_body is specified. if isinstance(if_else_op.condition, (Var | Unary | Binary)): # In this case, if_else_op.condition is a Binary operation which we must flatten - condition_flattened = flatten_condition(if_else_op.condition) + condition_flattened = _flatten_condition(if_else_op.condition) outer_builder.tkc.add_circbox( circbox=CircBox(if_circ), From 86d6225ec4c31b17683841c17ed8f4a6059aa012 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Tue, 30 Sep 2025 09:57:25 +0100 Subject: [PATCH 12/14] fix args --- pytket/extensions/qiskit/qiskit_convert.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index 242c1cc6..9d58057a 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -643,11 +643,11 @@ def _flatten_condition( _condition.right, bits ) if _condition.op.name == "BIT_OR": - return _flatten_condition(_condition.left) | _flatten_condition( + return _flatten_condition(_condition.left, bits) | _flatten_condition( _condition.right, bits ) if _condition.op.name == "BIT_XOR": - return _flatten_condition(_condition.left) ^ _flatten_condition( + return _flatten_condition(_condition.lef, bits) ^ _flatten_condition( _condition.right, bits ) @@ -740,7 +740,7 @@ def _append_if_else_circuit( # else_circ can be None if no false_body is specified. if isinstance(if_else_op.condition, (Var | Unary | Binary)): # In this case, if_else_op.condition is a Binary operation which we must flatten - condition_flattened = _flatten_condition(if_else_op.condition) + condition_flattened = _flatten_condition(if_else_op.condition, bits) outer_builder.tkc.add_circbox( circbox=CircBox(if_circ), From 23f0b08a33fd1210ee49bac6da239c2a5fc8904a Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Tue, 30 Sep 2025 10:23:33 +0100 Subject: [PATCH 13/14] add type: ignore for classical.expr --- pytket/extensions/qiskit/qiskit_convert.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index 9d58057a..51cc0260 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -83,11 +83,7 @@ Reset, ) from qiskit.circuit import Qubit as QCQubit -from qiskit.circuit.classical.expr import ( - Binary, - Unary, - Var, -) +from qiskit.circuit.classical.expr import Binary, Unary, Var # type: ignore from qiskit.circuit.library import ( CRYGate, Initialize, From 767f7aab5950c619e1da11e07ffc967d48a24767 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Tue, 30 Sep 2025 10:26:39 +0100 Subject: [PATCH 14/14] fix typo --- pytket/extensions/qiskit/qiskit_convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index 51cc0260..61d0f026 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -643,7 +643,7 @@ def _flatten_condition( _condition.right, bits ) if _condition.op.name == "BIT_XOR": - return _flatten_condition(_condition.lef, bits) ^ _flatten_condition( + return _flatten_condition(_condition.left, bits) ^ _flatten_condition( _condition.right, bits )