-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
269 additions
and
0 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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
"""This module implements the DiscreteLayerGenerator class.""" | ||
from __future__ import annotations | ||
|
||
import logging | ||
from typing import Sequence | ||
|
||
from bqskit.compiler.passdata import PassData | ||
from bqskit.ir.circuit import Circuit | ||
from bqskit.ir.gate import Gate | ||
from bqskit.ir.gates import CNOTGate | ||
from bqskit.ir.gates import HGate | ||
from bqskit.ir.gates import TGate | ||
from bqskit.ir.gates.parameterized.pauliz import PauliZGate | ||
from bqskit.ir.operation import Operation | ||
from bqskit.passes.search.generator import LayerGenerator | ||
from bqskit.qis.state.state import StateVector | ||
from bqskit.qis.state.system import StateSystem | ||
from bqskit.qis.unitary.unitarymatrix import UnitaryMatrix | ||
from bqskit.utils.typing import is_sequence | ||
|
||
|
||
_logger = logging.getLogger(__name__) | ||
|
||
|
||
class DiscreteLayerGenerator(LayerGenerator): | ||
""" | ||
The DiscreteLayerGenerator class. | ||
Expands circuits using only discrete gates. | ||
""" | ||
|
||
def __init__( | ||
self, | ||
gateset: Sequence[Gate] = [HGate(), TGate(), CNOTGate()], | ||
double_headed: bool = False, | ||
) -> None: | ||
""" | ||
Construct a DiscreteLayerGenerator. | ||
Args: | ||
gateset (Sequence[Gate]): A sequence of gates that can be used | ||
in the output circuit. These must be non-parameterized gates. | ||
(Default: [HGate(), TGate(), CNOTGate()]) | ||
double_headed (bool): If True, successors will be generated by | ||
both appending and prepending gates. (Default: False) | ||
Raises: | ||
TypeError: If the gateset is not a sequence. | ||
TypeError: If the gateset contains a parameterized gate. | ||
TypeError: If the radices of gates are different. | ||
""" | ||
if not is_sequence(gateset): | ||
m = f'Expected sequence of gates, got {type(gateset)}.' | ||
raise TypeError(m) | ||
|
||
radix = gateset[0].radixes[0] | ||
for gate in gateset: | ||
if gate.num_params > 0: | ||
m = 'Expected gate for constant gates, got parameterized' | ||
m += f' {gate} gate.' | ||
raise TypeError(m) | ||
for rad in gate.radixes: | ||
if rad != radix: | ||
m = f'Radix mismatch on gate: {gate}. ' | ||
m += f'Expected {radix}, got {rad}.' | ||
raise TypeError(m) | ||
self.gateset = gateset | ||
self.double_headed = double_headed | ||
|
||
def gen_initial_layer( | ||
self, | ||
target: UnitaryMatrix | StateVector | StateSystem, | ||
data: PassData, | ||
) -> Circuit: | ||
""" | ||
Generate the initial layer, see LayerGenerator for more. | ||
Raises: | ||
ValueError: If `target` has a radix mismatch with | ||
`self.initial_layer_gate`. | ||
""" | ||
|
||
if not isinstance(target, (UnitaryMatrix, StateVector, StateSystem)): | ||
m = f'Expected unitary or state, got {type(target)}.' | ||
raise TypeError(m) | ||
|
||
for radix in target.radixes: | ||
if radix != self.gateset[0].radixes[0]: | ||
m = 'Radix mismatch between target and gateset.' | ||
raise ValueError(m) | ||
|
||
init_circuit = Circuit(target.num_qudits, target.radixes) | ||
|
||
if self.double_headed: | ||
n = target.num_qudits | ||
span = list(range(n)) | ||
init_circuit.append_gate(PauliZGate(n), span) | ||
|
||
return init_circuit | ||
|
||
def cancels_something( | ||
self, | ||
circuit: Circuit, | ||
gate: Gate, | ||
location: tuple[int, ...], | ||
) -> bool: | ||
"""Ensure applying gate at location does not cancel a previous gate.""" | ||
last_cycle = circuit.num_cycles - 1 | ||
try: | ||
op = circuit.get_operation((last_cycle, location[0])) | ||
op_gate, op_location = op.gate, op.location | ||
if op_location == location and op_gate.get_inverse() == gate: | ||
return True | ||
return False | ||
except IndexError: | ||
return False | ||
|
||
def count_repeats( | ||
self, | ||
circuit: Circuit, | ||
gate: Gate, | ||
qudit: int, | ||
) -> int: | ||
"""Count the number of times the last gate is repeated on qudit.""" | ||
count = 0 | ||
for cycle in reversed(range(circuit.num_cycles)): | ||
try: | ||
op = circuit.get_operation((cycle, qudit)) | ||
if op.gate == gate: | ||
count += 1 | ||
else: | ||
return count | ||
except IndexError: | ||
continue | ||
return count | ||
|
||
def gen_successors(self, circuit: Circuit, data: PassData) -> list[Circuit]: | ||
""" | ||
Generate the successors of a circuit node. | ||
Raises: | ||
ValueError: If circuit is a single-qudit circuit. | ||
""" | ||
if not isinstance(circuit, Circuit): | ||
raise TypeError(f'Expected circuit, got {type(circuit)}.') | ||
|
||
if circuit.num_qudits < 2: | ||
raise ValueError('Cannot expand a single-qudit circuit.') | ||
|
||
# Get the coupling graph | ||
coupling_graph = data.connectivity | ||
|
||
# Generate successors | ||
successors = [] | ||
hashes = set() | ||
singles = [gate for gate in self.gateset if gate.num_qudits == 1] | ||
multis = [gate for gate in self.gateset if gate.num_qudits > 1] | ||
|
||
for gate in singles: | ||
for qudit in range(circuit.num_qudits): | ||
if self.cancels_something(circuit, gate, (qudit,)): | ||
continue | ||
if isinstance(gate, TGate): | ||
if self.count_repeats(circuit, TGate(), qudit) >= 7: | ||
continue | ||
successor = circuit.copy() | ||
successor.append_gate(gate, [qudit]) | ||
|
||
h = hash_circuit_structure(successor) | ||
if h not in hashes: | ||
successors.append(successor) | ||
hashes.add(h) | ||
|
||
if self.double_headed: | ||
successor = circuit.copy() | ||
op = Operation(gate, [qudit]) | ||
successor.insert(0, op) | ||
h = hash_circuit_structure(successor) | ||
if h not in hashes: | ||
successors.append(successor) | ||
hashes.add(h) | ||
|
||
for gate in multis: | ||
for edge in coupling_graph: | ||
if self.cancels_something(circuit, gate, edge): | ||
continue | ||
successor = circuit.copy() | ||
successor.append_gate(gate, edge) | ||
h = hash_circuit_structure(successor) | ||
if h not in hashes: | ||
successors.append(successor) | ||
hashes.add(h) | ||
|
||
if self.double_headed: | ||
successor = circuit.copy() | ||
op = Operation(gate, edge) | ||
successor.insert(0, op) | ||
h = hash_circuit_structure(successor) | ||
if h not in hashes: | ||
successors.append(successor) | ||
hashes.add(h) | ||
|
||
return successors | ||
|
||
|
||
def hash_circuit_structure(circuit: Circuit) -> int: | ||
hashes = [] | ||
for op in circuit: | ||
hashes.append(hash(op)) | ||
return hash(tuple(hashes)) |
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,51 @@ | ||
from __future__ import annotations | ||
|
||
from random import randint | ||
|
||
from bqskit.compiler.passdata import PassData | ||
from bqskit.ir.circuit import Circuit | ||
from bqskit.ir.gates import CNOTGate | ||
from bqskit.ir.gates import HGate | ||
from bqskit.ir.gates import TGate | ||
from bqskit.passes.search.generators import DiscreteLayerGenerator | ||
|
||
|
||
class TestDiscreteLayerGenerator: | ||
|
||
def test_gate_set(self) -> None: | ||
gates = [HGate(), CNOTGate(), TGate()] | ||
generator = DiscreteLayerGenerator() | ||
assert all(g in generator.gateset for g in gates) | ||
|
||
def test_double_headed(self) -> None: | ||
single_gen = DiscreteLayerGenerator(double_headed=False) | ||
double_gen = DiscreteLayerGenerator(double_headed=True) | ||
base = Circuit(4) | ||
single_sucs = single_gen.gen_successors(base, PassData(base)) | ||
double_sucs = double_gen.gen_successors(base, PassData(base)) | ||
assert len(single_sucs) == len(double_sucs) | ||
|
||
base = Circuit(2) | ||
base.append_gate(CNOTGate(), (0, 1)) | ||
single_sucs = single_gen.gen_successors(base, PassData(base)) | ||
double_sucs = double_gen.gen_successors(base, PassData(base)) | ||
assert len(single_sucs) < len(double_sucs) | ||
assert all(c in double_sucs for c in single_sucs) | ||
|
||
def test_cancels_something(self) -> None: | ||
gen = DiscreteLayerGenerator() | ||
base = Circuit(2) | ||
base.append_gate(HGate(), (0,)) | ||
base.append_gate(TGate(), (0,)) | ||
base.append_gate(HGate(), (0,)) | ||
assert gen.cancels_something(base, HGate(), (0,)) | ||
assert not gen.cancels_something(base, HGate(), (1,)) | ||
assert not gen.cancels_something(base, TGate(), (0,)) | ||
|
||
def test_count_repeats(self) -> None: | ||
num_repeats = randint(1, 50) | ||
c = Circuit(1) | ||
for _ in range(num_repeats): | ||
c.append_gate(HGate(), (0,)) | ||
gen = DiscreteLayerGenerator() | ||
assert gen.count_repeats(c, HGate(), 0) == num_repeats |