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

72 handle measure instructions and bit registers #144

Merged
merged 8 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 11 additions & 12 deletions opensquirrel/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

import numpy as np

import opensquirrel.parsing.antlr.squirrel_ir_from_string
from opensquirrel import circuit_matrix_calculator, mckay_decomposer, merger, replacer, writer
from opensquirrel.default_gates import default_gate_aliases, default_gate_set
from opensquirrel.default_measurements import default_measurement_set
from opensquirrel.export import quantify_scheduler_exporter
from opensquirrel.export_format import ExportFormat
from opensquirrel.parsing.libqasm.libqasm_ir_creator import LibqasmIRCreator
from opensquirrel.replacer import Decomposer
from opensquirrel.squirrel_ir import Gate, SquirrelIR
from opensquirrel.squirrel_ir import Gate, Measure, SquirrelIR


class Circuit:
Expand Down Expand Up @@ -48,7 +48,7 @@ def from_string(
cqasm3_string: str,
gate_set: [Callable[..., Gate]] = default_gate_set,
gate_aliases: Dict[str, Callable[..., Gate]] = default_gate_aliases,
use_libqasm: bool = False,
measurement_set: [Callable[..., Measure]] = default_measurement_set,
):
"""Create a circuit object from a cQasm3 string. All the gates in the circuit need to be defined in
the `gates` argument.
Expand All @@ -62,20 +62,19 @@ def from_string(
Args:
cqasm3_string: a cqasm 3 string
gate_set: an array of gate semantic functions. See default_gates for examples
gate_aliases: a dictionary of extra aliases, mapping strings to functions in the gate set
gate_aliases: a dictionary of extra gate aliases, mapping strings to functions in the gate set
measurement_set: an array of measurement semantic functions. See default_measurements for examples
measurement_aliases: a dictionary of measure aliases, mapping strings to functions in the measurement set
use_libqasm: if True, use libqasm instead of build-in ANTLR parser.
Note: those two separate implementations may diverge and libqasm should be taken as reference.

"""
if use_libqasm:
libqasm_ir_creator = LibqasmIRCreator(gate_set=gate_set, gate_aliases=gate_aliases)
return Circuit(libqasm_ir_creator.squirrel_ir_from_string(cqasm3_string))

return Circuit(
opensquirrel.parsing.antlr.squirrel_ir_from_string.squirrel_ir_from_string(
cqasm3_string, gate_set=gate_set, gate_aliases=gate_aliases
)
libqasm_ir_creator = LibqasmIRCreator(
gate_set=gate_set,
gate_aliases=gate_aliases,
measurement_set=measurement_set,
)
return Circuit(libqasm_ir_creator.squirrel_ir_from_string(cqasm3_string))

@property
def number_of_qubits(self) -> int:
Expand Down
2 changes: 1 addition & 1 deletion opensquirrel/circuit_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from opensquirrel.circuit import Circuit
from opensquirrel.default_gates import default_gate_aliases, default_gate_set
from opensquirrel.gate_library import GateLibrary
from opensquirrel.instruction_library import GateLibrary
from opensquirrel.squirrel_ir import Comment, Gate, Qubit, SquirrelIR


Expand Down
14 changes: 14 additions & 0 deletions opensquirrel/default_measurements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from opensquirrel.squirrel_ir import *


@named_measurement
def measure(q: Qubit) -> Measure:
return Measure(qubit=q, axis=(0, 0, 1))


@named_measurement
def measure_z(q: Qubit) -> Measure:
return Measure(qubit=q, axis=(0, 0, 1))


default_measurement_set = [measure_z, measure]
13 changes: 0 additions & 13 deletions opensquirrel/gate_library.py

This file was deleted.

29 changes: 29 additions & 0 deletions opensquirrel/instruction_library.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
class InstructionLibrary:
pass


class GateLibrary(InstructionLibrary):
def __init__(self, gate_set, gate_aliases):
self.gate_set = gate_set
self.gate_aliases = gate_aliases

def get_gate_f(self, gate_name: str):
try:
generator_f = next(f for f in self.gate_set if f.__name__ == gate_name)
except StopIteration:
if gate_name not in self.gate_aliases:
raise Exception(f"Unknown gate `{gate_name}`")
generator_f = self.gate_aliases[gate_name]
return generator_f


class MeasurementLibrary(InstructionLibrary):
def __init__(self, measurement_set):
self.measurement_set = measurement_set

def get_measurement_f(self, measurement_name: str):
try:
generator_f = next(f for f in self.measurement_set if f.__name__ == measurement_name)
return generator_f
except StopIteration:
raise Exception(f"Unknown measurement `{measurement_name}`")
8 changes: 5 additions & 3 deletions opensquirrel/merger.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import numpy as np

from opensquirrel.common import ATOL
from opensquirrel.squirrel_ir import BlochSphereRotation, Gate, Qubit, SquirrelIR
from opensquirrel.squirrel_ir import BlochSphereRotation, Gate, Measure, Qubit, SquirrelIR


def compose_bloch_sphere_rotations(a: BlochSphereRotation, b: BlochSphereRotation) -> BlochSphereRotation:
Expand Down Expand Up @@ -59,8 +59,8 @@ def merge_single_qubit_gates(squirrel_ir: SquirrelIR):
while statement_index < len(squirrel_ir.statements):
statement = squirrel_ir.statements[statement_index]

if not isinstance(statement, Gate):
# Skip, since statement is not a gate
if not isinstance(statement, Gate) and not isinstance(statement, Measure):
# Skip, since statement is not a gate or measurement
statement_index += 1
continue

Expand All @@ -84,3 +84,5 @@ def merge_single_qubit_gates(squirrel_ir: SquirrelIR):
for accumulated_bloch_sphere_rotation in accumulators_per_qubit.values():
if not accumulated_bloch_sphere_rotation.is_identity():
squirrel_ir.statements.append(accumulated_bloch_sphere_rotation)

squirrel_ir.statements = sorted(squirrel_ir.statements, key=lambda obj: isinstance(obj, Measure))
elenbaasc marked this conversation as resolved.
Show resolved Hide resolved
28 changes: 24 additions & 4 deletions opensquirrel/parsing/libqasm/libqasm_ir_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import cqasm.v3x as cqasm

from opensquirrel.default_gates import default_gate_aliases, default_gate_set
from opensquirrel.gate_library import GateLibrary
from opensquirrel.default_measurements import default_measurement_set
from opensquirrel.instruction_library import GateLibrary, MeasurementLibrary
from opensquirrel.squirrel_ir import Float, Int, Qubit, SquirrelIR

_cqasm_type_to_squirrel_type = {
Expand All @@ -14,9 +15,15 @@
}


class LibqasmIRCreator(GateLibrary):
def __init__(self, gate_set=default_gate_set, gate_aliases=default_gate_aliases):
class LibqasmIRCreator(GateLibrary, MeasurementLibrary):
def __init__(
self,
gate_set=default_gate_set,
gate_aliases=default_gate_aliases,
measurement_set=default_measurement_set,
):
GateLibrary.__init__(self, gate_set, gate_aliases)
MeasurementLibrary.__init__(self, measurement_set)
self.squirrel_ir = None

@staticmethod
Expand Down Expand Up @@ -96,6 +103,16 @@ def _create_analyzer(self):
param_types = "".join(set_of_letters)
analyzer.register_instruction(generator_f.__name__, param_types)

for generator_f in self.measurement_set:
for set_of_letters in itertools.product(
*(
self._get_cqasm_param_type_letters(p.annotation)
for p in inspect.signature(generator_f).parameters.values()
)
):
param_types = "".join(set_of_letters)
analyzer.register_instruction(generator_f.__name__, param_types)

return analyzer

def squirrel_ir_from_string(self, s: str):
Expand All @@ -114,7 +131,10 @@ def squirrel_ir_from_string(self, s: str):
squirrel_ir = SquirrelIR(number_of_qubits=number_of_qubits, qubit_register_name=qubit_register_name)

for statement in ast.block.statements:
generator_f = self.get_gate_f(statement.name[2:-1])
if "measure" in statement.name[2:-1]:
generator_f = self.get_measurement_f(statement.name[2:-1])
else:
generator_f = self.get_gate_f(statement.name[2:-1])

expanded_squirrel_args = LibqasmIRCreator._get_expanded_squirrel_args(generator_f, statement.operands)

Expand Down
73 changes: 71 additions & 2 deletions opensquirrel/squirrel_ir.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ def visit_qubit(self, qubit: "Qubit"):
def visit_gate(self, gate: "Gate"):
pass

def visit_measure(self, measure: "Measure"):
pass

def visit_bloch_sphere_rotation(self, bloch_sphere_rotation: "BlochSphereRotation"):
pass

Expand Down Expand Up @@ -81,6 +84,41 @@ class Statement(IRNode, ABC):
pass


class Measure(Statement, ABC):
generator: Optional[Callable[..., "Measure"]] = None
arguments: Optional[Tuple[Expression, ...]] = None

def __init__(
self,
qubit: Qubit,
axis: Tuple[float, float, float],
generator=None,
arguments=None,
):
self.generator = generator
self.arguments = arguments
self.qubit: Qubit = qubit
self.axis = normalize_axis(np.array(axis).astype(np.float64))

def __repr__(self):
return f"Measure({self.qubit}, axis={self.axis})"

@property
def name(self) -> Optional[str]:
return self.generator.__name__ if self.generator else "<abstract_measurement>"

def __eq__(self, other):
if not isinstance(other, Measure):
return False
return self.qubit == other.qubit and self.axis == other.axis

def accept(self, visitor: SquirrelIRVisitor):
visitor.visit_measure(self)

def get_qubit_operands(self) -> List[Qubit]:
return [self.qubit]


class Gate(Statement, ABC):
# Note: two gates are considered equal even when their generators/arguments are different.
generator: Optional[Callable[..., "Gate"]] = None
Expand Down Expand Up @@ -213,7 +251,6 @@ def is_identity(self) -> bool:


def _compare_gate_classes(g1: Gate, g2: Gate) -> bool:

union_mapping = list(set(g1.get_qubit_operands()) | set(g2.get_qubit_operands()))

from opensquirrel.utils.matrix_expander import get_matrix_after_qubit_remapping
Expand Down Expand Up @@ -248,6 +285,30 @@ def wrapper(*args, **kwargs):
return wrapper


def named_measurement(measurement_generator: Callable[..., Measure]) -> Callable[..., Measure]:
@wraps(measurement_generator)
def wrapper(*args, **kwargs):
result = measurement_generator(*args, **kwargs)
result.generator = wrapper

all_args = []
arg_index = 0
for par in inspect.signature(measurement_generator).parameters.values():
if not issubclass(par.annotation, Expression):
raise TypeError("Measurement argument types must be expressions")

if par.name in kwargs:
all_args.append(kwargs[par.name])
else:
all_args.append(args[arg_index])
arg_index += 1

result.arguments = tuple(all_args)
return result

return wrapper


@dataclass
class Comment(Statement):
str: str
Expand All @@ -261,14 +322,22 @@ def accept(self, visitor: SquirrelIRVisitor):

class SquirrelIR:
# This is just a list of gates (for now?)
def __init__(self, *, number_of_qubits: int, qubit_register_name: str = "q"):
def __init__(
self,
*,
number_of_qubits: int,
qubit_register_name: str = "q",
):
self.number_of_qubits: int = number_of_qubits
self.statements: List[Statement] = []
self.qubit_register_name: str = qubit_register_name

def add_gate(self, gate: Gate):
self.statements.append(gate)

def add_measurement(self, measurement: Measure):
self.statements.append(measurement)

def add_comment(self, comment: Comment):
self.statements.append(comment)

Expand Down
5 changes: 4 additions & 1 deletion opensquirrel/writer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from opensquirrel.squirrel_ir import Comment, Float, Gate, Int, Qubit, SquirrelIR, SquirrelIRVisitor
from opensquirrel.squirrel_ir import Comment, Float, Gate, Int, Measure, Qubit, SquirrelIR, SquirrelIRVisitor


class _WriterImpl(SquirrelIRVisitor):
Expand All @@ -17,6 +17,9 @@ def visit_int(self, i: Int):
def visit_float(self, f: Float):
return f"{f.value:.{self.number_of_significant_digits}}"

def visit_measure(self, measurement: Measure):
self.output += f"{measurement.name} {measurement.arguments[0].accept(self)}\n"

def visit_gate(self, gate: Gate):
if gate.is_anonymous:
self.output += "<anonymous-gate>\n"
Expand Down
Loading