-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add builder interface for new switch statement
With the addition of `switch`, we now have a second control-flow construct that - is not a loop, so can have lazily constructed blocks because of contained `break` and `continue` statements - can have multiple blocks that need their resources unifying For the first part, we largely just have to handle things separately to `if`, though the placeholder instructions are much simpler because the placeholder will never need mutating and replacing (c.f. the `else` block). For the second point, we move a bunch of the previously if-specific code into a shared location in order to reuse it. Unlike the `if` case, the `switch` builder uses nested context managers for its cases, because this mirrors a common indentation pattern for switch statements (at least if you're not a Linux kernel developer). We need new special logic to reject statements that are loose in the `switch` context but not a `case` context. We _could_ have just ignored that, but it feels like an easy potential mistake to make, so much better to loudly fail than silently do the wrong thing.
- Loading branch information
1 parent
c0e4141
commit d0fab50
Showing
6 changed files
with
962 additions
and
146 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# This code is part of Qiskit. | ||
# | ||
# (C) Copyright IBM 2022. | ||
# | ||
# This code is licensed under the Apache License, Version 2.0. You may | ||
# obtain a copy of this license in the LICENSE.txt file in the root directory | ||
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||
# | ||
# Any modifications or derivative works of this code must retain this | ||
# copyright notice, and modified files need to carry a notice indicating | ||
# that they have been altered from the originals. | ||
|
||
"""Private utility functions that are used by the builder interfaces.""" | ||
|
||
from typing import Iterable, Tuple, Set | ||
|
||
from qiskit.circuit.exceptions import CircuitError | ||
from qiskit.circuit.quantumcircuit import QuantumCircuit | ||
from qiskit.circuit.register import Register | ||
from qiskit.circuit.classicalregister import ClassicalRegister | ||
from qiskit.circuit.quantumregister import QuantumRegister | ||
|
||
|
||
def partition_registers( | ||
registers: Iterable[Register], | ||
) -> Tuple[Set[QuantumRegister], Set[ClassicalRegister]]: | ||
"""Partition a sequence of registers into its quantum and classical registers.""" | ||
qregs = set() | ||
cregs = set() | ||
for register in registers: | ||
if isinstance(register, QuantumRegister): | ||
qregs.add(register) | ||
elif isinstance(register, ClassicalRegister): | ||
cregs.add(register) | ||
else: | ||
# Purely defensive against Terra expansion. | ||
raise CircuitError(f"Unknown register: {register}.") | ||
return qregs, cregs | ||
|
||
|
||
def unify_circuit_resources(circuits: Iterable[QuantumCircuit]) -> Iterable[QuantumCircuit]: | ||
""" | ||
Ensure that all the given ``circuits`` have all the same qubits, clbits and registers, and | ||
that they are defined in the same order. The order is important for binding when the bodies are | ||
used in the 3-tuple :obj:`.Instruction` context. | ||
This function will preferentially try to mutate its inputs if they share an ordering, but if | ||
not, it will rebuild two new circuits. This is to avoid coupling too tightly to the inner | ||
class; there is no real support for deleting or re-ordering bits within a :obj:`.QuantumCircuit` | ||
context, and we don't want to rely on the *current* behaviour of the private APIs, since they | ||
are very liable to change. No matter the method used, circuits with unified bits and registers | ||
are returned. | ||
""" | ||
circuits = tuple(circuits) | ||
if len(circuits) < 2: | ||
return circuits | ||
qubits = [] | ||
clbits = [] | ||
for circuit in circuits: | ||
if circuit.qubits[: len(qubits)] != qubits: | ||
return _unify_circuit_resources_rebuild(circuits) | ||
if circuit.clbits[: len(qubits)] != clbits: | ||
return _unify_circuit_resources_rebuild(circuits) | ||
if circuit.num_qubits > len(qubits): | ||
qubits = list(circuit.qubits) | ||
if circuit.num_clbits > len(clbits): | ||
clbits = list(circuit.clbits) | ||
for circuit in circuits: | ||
circuit.add_bits(qubits[circuit.num_qubits :]) | ||
circuit.add_bits(clbits[circuit.num_clbits :]) | ||
return _unify_circuit_registers(circuits) | ||
|
||
|
||
def _unify_circuit_resources_rebuild( # pylint: disable=invalid-name # (it's too long?!) | ||
circuits: Tuple[QuantumCircuit, ...] | ||
) -> Tuple[QuantumCircuit, QuantumCircuit]: | ||
""" | ||
Ensure that all the given circuits have all the same qubits and clbits, and that they | ||
are defined in the same order. The order is important for binding when the bodies are used in | ||
the 3-tuple :obj:`.Instruction` context. | ||
This function will always rebuild the objects into new :class:`.QuantumCircuit` instances. | ||
""" | ||
qubits, clbits = set(), set() | ||
for circuit in circuits: | ||
qubits.update(circuit.qubits) | ||
clbits.update(circuit.clbits) | ||
qubits, clbits = list(qubits), list(clbits) | ||
|
||
# We use the inner `_append` method because everything is already resolved in the builders. | ||
out_circuits = [] | ||
for circuit in circuits: | ||
out = QuantumCircuit(qubits, clbits, *circuit.qregs, *circuit.cregs) | ||
for instruction in circuit.data: | ||
out._append(instruction) | ||
out_circuits.append(out) | ||
return _unify_circuit_registers(out_circuits) | ||
|
||
|
||
def _unify_circuit_registers(circuits: Iterable[QuantumCircuit]) -> Iterable[QuantumCircuit]: | ||
""" | ||
Ensure that ``true_body`` and ``false_body`` have the same registers defined within them. These | ||
do not need to be in the same order between circuits. The two input circuits are returned, | ||
mutated to have the same registers. | ||
""" | ||
circuits = tuple(circuits) | ||
total_registers = set() | ||
for circuit in circuits: | ||
total_registers.update(circuit.qregs) | ||
total_registers.update(circuit.cregs) | ||
for circuit in circuits: | ||
for register in total_registers - set(circuit.qregs) - set(circuit.cregs): | ||
circuit.add_register(register) | ||
return circuits |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.