Skip to content
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

Adding Cliffords to QuantumCircuits as Operations #7966

Merged
merged 41 commits into from
Aug 10, 2022
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
068525a
Adding Clifford to a QuantumCircuit natively; adding some tests; addi…
alexanderivrii Apr 20, 2022
cd8367b
Merge branch 'main' into operation
alexanderivrii Apr 27, 2022
2ab3084
Making Instruction.condition into a property
alexanderivrii Apr 27, 2022
dffc618
improving tests
alexanderivrii Apr 27, 2022
ea56a13
adding release notes
alexanderivrii Apr 27, 2022
f3af0b6
A few changes based on review
alexanderivrii May 10, 2022
2640f5d
Merge branch 'main' into operation
alexanderivrii May 10, 2022
568d8b8
Merge branch 'main' into operation
alexanderivrii May 11, 2022
ec342e5
Merge branch 'main' into operation
alexanderivrii May 15, 2022
c16e85f
Merge branch 'main' into operation
alexanderivrii May 17, 2022
015cbe9
Merge branch 'main' into operation
alexanderivrii May 17, 2022
e35a3d5
resolving merge conflicts
alexanderivrii Jun 7, 2022
15ce409
Removing definition from Operation interface. Instead adding
alexanderivrii Jun 7, 2022
d59dd7e
minor lint fix
alexanderivrii Jun 7, 2022
51aff6d
moving all of broadcast functionality to a separate file; removing br…
alexanderivrii Jun 7, 2022
2c3b908
moving HighLevelSynthesis pass from Decompose to QuantumCircuit.decom…
alexanderivrii Jun 7, 2022
2a87222
Resolving conflcts, reverting broadcasting (temporarily breaks functi…
alexanderivrii Jul 2, 2022
14ddc7b
Fixing broadcasting for Cliffords
alexanderivrii Jul 2, 2022
4949a1d
making sure that transpile decomposes cliffords
alexanderivrii Jul 2, 2022
c1ab860
lint
alexanderivrii Jul 2, 2022
aeef894
As per code review, replacing x._direction by getattr(x, '_direction'…
alexanderivrii Jul 3, 2022
9f019c2
Removing condition from Operation API.
alexanderivrii Jul 5, 2022
c80efa9
pass over condition in transpiler code
alexanderivrii Jul 5, 2022
4b1a246
more refactoring of condition
alexanderivrii Jul 5, 2022
5ae37a5
finishing condition pass
alexanderivrii Jul 5, 2022
4a5f06f
Merge branch 'main' into operation
alexanderivrii Jul 5, 2022
34eca8c
resolving merge conflicts
alexanderivrii Aug 4, 2022
de6110d
resolving merge conflicts
alexanderivrii Aug 4, 2022
7a7b0f1
minor fixes
alexanderivrii Aug 5, 2022
76acd6b
Merge branch 'main' into operation
alexanderivrii Aug 5, 2022
64c8de3
adding OptimizeClifford pass to __init__
alexanderivrii Aug 6, 2022
b2f972a
Improving release notes
alexanderivrii Aug 6, 2022
0d583ed
considering DAG nodes in topological order; adding a simple test to s…
alexanderivrii Aug 6, 2022
bdf9325
typo
alexanderivrii Aug 6, 2022
49bc436
release notes fixes
alexanderivrii Aug 6, 2022
7f525d2
Adding TODO comment to HighLevelSynthesis pass
alexanderivrii Aug 6, 2022
d82e2e0
another attempt to fix docs build
alexanderivrii Aug 6, 2022
07733d6
Fix based on code review
alexanderivrii Aug 9, 2022
ac1c874
Merge branch 'main' into operation
alexanderivrii Aug 10, 2022
ab213f2
Only construction Operator from Instruction (as before) and from Clif…
alexanderivrii Aug 10, 2022
fb57c9d
Merge branch 'operation' of github.com:alexanderivrii/qiskit-terra in…
alexanderivrii Aug 10, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion qiskit/assembler/assemble_circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ def _assemble_circuit(
# their clbit_index, create a new register slot for every conditional gate
# and add a bfunc to map the creg=val mask onto the gating register bit.

is_conditional_experiment = any(instruction.operation.condition for instruction in circuit.data)
is_conditional_experiment = any(
getattr(instruction.operation, "condition", None) for instruction in circuit.data
)
max_conditional_idx = 0

instructions = []
Expand Down
2 changes: 2 additions & 0 deletions qiskit/circuit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@
Delay
Instruction
InstructionSet
Operation
EquivalenceLibrary

Control Flow Operations
Expand Down Expand Up @@ -232,6 +233,7 @@
from .controlledgate import ControlledGate
from .instruction import Instruction
from .instructionset import InstructionSet
from .operation import Operation
from .barrier import Barrier
from .delay import Delay
from .measure import Measure
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/controlflow/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ def build(
# a register is already present, so we use our own tracking.
self.add_register(register)
out.add_register(register)
if instruction.operation.condition is not None:
if getattr(instruction.operation, "condition", None) is not None:
for register in condition_registers(instruction.operation.condition):
if register not in self.registers:
self.add_register(register)
Expand Down
3 changes: 2 additions & 1 deletion qiskit/circuit/instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,13 @@
from qiskit.circuit.classicalregister import ClassicalRegister, Clbit
from qiskit.qobj.qasm_qobj import QasmQobjInstruction
from qiskit.circuit.parameter import ParameterExpression
from qiskit.circuit.operation import Operation
from .tools import pi_check

_CUTOFF_PRECISION = 1e-10


class Instruction:
class Instruction(Operation):
"""Generic quantum instruction."""

# Class attribute to treat like barrier for transpiler, unroller, drawer
Expand Down
6 changes: 3 additions & 3 deletions qiskit/circuit/instructionset.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
from typing import Callable, Optional, Tuple, Union

from qiskit.circuit.exceptions import CircuitError
from .instruction import Instruction
from .classicalregister import Clbit, ClassicalRegister
from .operation import Operation
from .quantumcircuitdata import CircuitInstruction


Expand Down Expand Up @@ -150,8 +150,8 @@ def __getitem__(self, i):
def add(self, instruction, qargs=None, cargs=None):
"""Add an instruction and its context (where it is attached)."""
if not isinstance(instruction, CircuitInstruction):
if not isinstance(instruction, Instruction):
raise CircuitError("attempt to add non-Instruction to InstructionSet")
if not isinstance(instruction, Operation):
raise CircuitError("attempt to add non-Operation to InstructionSet")
if qargs is None or cargs is None:
raise CircuitError("missing qargs or cargs in old-style InstructionSet.add")
instruction = CircuitInstruction(instruction, tuple(qargs), tuple(cargs))
Expand Down
80 changes: 53 additions & 27 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
from .parametertable import ParameterReferences, ParameterTable, ParameterView
from .parametervector import ParameterVector, ParameterVectorElement
from .instructionset import InstructionSet
from .operation import Operation
from .register import Register
from .bit import Bit
from .quantumcircuitdata import QuantumCircuitData, CircuitInstruction
Expand Down Expand Up @@ -947,7 +948,7 @@ def compose(
n_cargs = [edge_map[carg] for carg in instr.clbits]
n_instr = instr.operation.copy()

if instr.operation.condition is not None:
if getattr(instr.operation, "condition", None) is not None:
from qiskit.dagcircuit import DAGCircuit # pylint: disable=cyclic-import

n_instr.condition = DAGCircuit._map_condition(
Expand Down Expand Up @@ -1216,7 +1217,7 @@ def _resolve_classical_resource(self, specifier):

def append(
self,
instruction: Union[Instruction, CircuitInstruction],
instruction: Union[Operation, CircuitInstruction],
qargs: Optional[Sequence[QubitSpecifier]] = None,
cargs: Optional[Sequence[ClbitSpecifier]] = None,
) -> InstructionSet:
Expand Down Expand Up @@ -1252,20 +1253,20 @@ def append(
else:
operation = instruction
# Convert input to instruction
if not isinstance(operation, Instruction) and not hasattr(operation, "to_instruction"):
if issubclass(operation, Instruction):
if not isinstance(operation, Operation) and not hasattr(operation, "to_instruction"):
if issubclass(operation, Operation):
raise CircuitError(
"Object is a subclass of Instruction, please add () to "
"Object is a subclass of Operation, please add () to "
"pass an instance of this object."
)

raise CircuitError(
"Object to append must be an Instruction or have a to_instruction() method."
"Object to append must be an Operation or have a to_instruction() method."
)
if not isinstance(operation, Instruction) and hasattr(operation, "to_instruction"):
if not isinstance(operation, Operation) and hasattr(operation, "to_instruction"):
operation = operation.to_instruction()
if not isinstance(operation, Instruction):
raise CircuitError("object is not an Instruction.")
if not isinstance(operation, Operation):
raise CircuitError("object is not an Operation.")

# Make copy of parameterized gate instances
if hasattr(operation, "params"):
Expand All @@ -1283,11 +1284,21 @@ def append(
appender = self._append
requester = self._resolve_classical_resource
instructions = InstructionSet(resource_requester=requester)
for qarg, carg in operation.broadcast_arguments(expanded_qargs, expanded_cargs):
self._check_dups(qarg)
instruction = CircuitInstruction(operation, qarg, carg)
appender(instruction)
instructions.add(instruction)
if isinstance(operation, Instruction):
for qarg, carg in operation.broadcast_arguments(expanded_qargs, expanded_cargs):
self._check_dups(qarg)
instruction = CircuitInstruction(operation, qarg, carg)
appender(instruction)
instructions.add(instruction)
else:
# For Operations that are non-Instructions, we use the Instruction's default method
for qarg, carg in Instruction.broadcast_arguments(
operation, expanded_qargs, expanded_cargs
):
self._check_dups(qarg)
instruction = CircuitInstruction(operation, qarg, carg)
appender(instruction)
instructions.add(instruction)
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
return instructions

# Preferred new style.
Expand All @@ -1301,10 +1312,10 @@ def _append(
@typing.overload
def _append(
self,
operation: Instruction,
operation: Operation,
qargs: Sequence[Qubit],
cargs: Sequence[Clbit],
) -> Instruction:
) -> Operation:
...

def _append(self, instruction, qargs=None, cargs=None):
Expand All @@ -1328,20 +1339,22 @@ def _append(self, instruction, qargs=None, cargs=None):
constructs of the control-flow builder interface.
Args:
instruction: Instruction instance to append
instruction: Operation instance to append
qargs: Qubits to attach the instruction to.
cargs: Clbits to attach the instruction to.
Returns:
Instruction: a handle to the instruction that was just added
Operation: a handle to the instruction that was just added
:meta public:
"""
old_style = not isinstance(instruction, CircuitInstruction)
if old_style:
instruction = CircuitInstruction(instruction, qargs, cargs)
self._data.append(instruction)
self._update_parameter_table(instruction)
if isinstance(instruction.operation, Instruction):
self._update_parameter_table(instruction)

# mark as normal circuit if a new instruction is added
self.duration = None
self.unit = "dt"
Expand Down Expand Up @@ -1567,11 +1580,13 @@ def decompose(
"""
# pylint: disable=cyclic-import
from qiskit.transpiler.passes.basis.decompose import Decompose
from qiskit.transpiler.passes.synthesis import HighLevelSynthesis
from qiskit.converters.circuit_to_dag import circuit_to_dag
from qiskit.converters.dag_to_circuit import dag_to_circuit

pass_ = Decompose(gates_to_decompose)
dag = circuit_to_dag(self)
dag = HighLevelSynthesis().run(dag)
pass_ = Decompose(gates_to_decompose)
for _ in range(reps):
dag = pass_.run(dag)
return dag_to_circuit(dag)
Expand Down Expand Up @@ -1936,7 +1951,10 @@ def draw(
)

def size(
self, filter_function: Optional[callable] = lambda x: not x.operation._directive
self,
filter_function: Optional[callable] = lambda x: not getattr(
x.operation, "_directive", False
),
) -> int:
"""Returns total number of instructions in circuit.
Expand All @@ -1951,7 +1969,10 @@ def size(
return sum(map(filter_function, self._data))

def depth(
self, filter_function: Optional[callable] = lambda x: not x.operation._directive
self,
filter_function: Optional[callable] = lambda x: not getattr(
x.operation, "_directive", False
),
) -> int:
"""Return circuit depth (i.e., length of critical path).
Expand Down Expand Up @@ -2000,7 +2021,7 @@ def depth(
levels.append(op_stack[reg_ints[ind]])
# Assuming here that there is no conditional
# snapshots or barriers ever.
if instruction.operation.condition:
if getattr(instruction.operation, "condition", None):
# Controls operate over all bits of a classical register
# or over a single bit
if isinstance(instruction.operation.condition[0], Clbit):
Expand Down Expand Up @@ -2064,7 +2085,9 @@ def num_nonlocal_gates(self) -> int:
"""
multi_qubit_gates = 0
for instruction in self._data:
if instruction.operation.num_qubits > 1 and not instruction.operation._directive:
if instruction.operation.num_qubits > 1 and not getattr(
instruction.operation, "_directive", False
):
multi_qubit_gates += 1
return multi_qubit_gates

Expand Down Expand Up @@ -2105,9 +2128,11 @@ def num_connected_components(self, unitary_only: bool = False) -> int:
num_qargs = len(args)
else:
args = instruction.qubits + instruction.clbits
num_qargs = len(args) + (1 if instruction.operation.condition else 0)
num_qargs = len(args) + (
1 if getattr(instruction.operation, "condition", None) else 0
)

if num_qargs >= 2 and not instruction.operation._directive:
if num_qargs >= 2 and not getattr(instruction.operation, "_directive", False):
graphs_touched = []
num_touched = 0
# Controls necessarily join all the cbits in the
Expand Down Expand Up @@ -4206,7 +4231,8 @@ def _pop_previous_instruction_in_scope(self) -> CircuitInstruction:
if not self._data:
raise CircuitError("This circuit contains no instructions.")
instruction = self._data.pop()
self._update_parameter_table_on_instruction_removal(instruction)
if isinstance(instruction.operation, Instruction):
self._update_parameter_table_on_instruction_removal(instruction)
return instruction

def _update_parameter_table_on_instruction_removal(self, instruction: CircuitInstruction):
Expand Down
7 changes: 6 additions & 1 deletion qiskit/circuit/quantumcircuitdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,12 @@ def _resolve_legacy_value(self, operation, qargs, cargs) -> CircuitInstruction:
expanded_qargs = [self._circuit.qbit_argument_conversion(qarg) for qarg in qargs or []]
expanded_cargs = [self._circuit.cbit_argument_conversion(carg) for carg in cargs or []]

broadcast_args = list(operation.broadcast_arguments(expanded_qargs, expanded_cargs))
if isinstance(operation, Instruction):
broadcast_args = list(operation.broadcast_arguments(expanded_qargs, expanded_cargs))
else:
broadcast_args = list(
Instruction.broadcast_arguments(operation, expanded_qargs, expanded_cargs)
)

if len(broadcast_args) > 1:
raise CircuitError(
Expand Down
2 changes: 1 addition & 1 deletion qiskit/converters/circuit_to_instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None

# fix condition
for rule in definition:
condition = rule.operation.condition
condition = getattr(rule.operation, "condition", None)
if condition:
reg, val = condition
if isinstance(reg, Clbit):
Expand Down
Loading