Skip to content

72 handle measure instructions and bit registers #144

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

Merged
merged 8 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
24 changes: 19 additions & 5 deletions opensquirrel/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
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_aliases, 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 +49,9 @@ 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,
measurement_aliases: Dict[str, Callable[..., Measure]] = default_measurement_aliases,
use_libqasm: bool = True,
):
"""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,18 +65,29 @@ 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)
libqasm_ir_creator = LibqasmIRCreator(
gate_set=gate_set,
gate_aliases=gate_aliases,
measurement_aliases=measurement_aliases,
measurement_set=measurement_set,
)
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
cqasm3_string,
gate_set=gate_set,
gate_aliases=gate_aliases,
measurement_set=measurement_set,
measurement_aliases=measurement_aliases,
)
)

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.operation_library import GateLibrary
from opensquirrel.squirrel_ir import Comment, Gate, Qubit, SquirrelIR


Expand Down
30 changes: 30 additions & 0 deletions opensquirrel/default_measurements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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))


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


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


default_measurement_set = [measure_x, measure_y, measure_z, measure]
default_measurement_aliases = {
"measure_x": measure_x,
"measure_y": measure_y,
"measure_z": measure_z,
"measure": measure,
}
13 changes: 0 additions & 13 deletions opensquirrel/gate_library.py

This file was deleted.

10 changes: 7 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 @@ -74,6 +74,7 @@ def merge_single_qubit_gates(squirrel_ir: SquirrelIR):
del squirrel_ir.statements[statement_index]
continue


for qubit_operand in statement.get_qubit_operands():
if not accumulators_per_qubit[qubit_operand].is_identity():
squirrel_ir.statements.insert(statement_index, accumulators_per_qubit[qubit_operand])
Expand All @@ -84,3 +85,6 @@ 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))

32 changes: 32 additions & 0 deletions opensquirrel/operation_library.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
class OperationLibrary:
pass


class GateLibrary(OperationLibrary):
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(OperationLibrary):
def __init__(self, measurement_set, measurement_aliases):
self.measurement_set = measurement_set
self.measurement_aliases = measurement_aliases

def get_measurement_f(self, measurement_name: str):
try:
generator_f = next(f for f in self.measurement_set if f.__name__ == measurement_name)
except StopIteration:
if measurement_name not in self.measurement_aliases:
raise Exception(f"Unknown measurement `{measurement_name}`")
generator_f = self.measurement_aliases[measurement_name]
return generator_f
2 changes: 2 additions & 0 deletions opensquirrel/parsing/antlr/generated/CQasm3Parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -1030,6 +1030,8 @@ def exitRule(self, listener: ParseTreeListener):

def accept(self, visitor: ParseTreeVisitor):
if hasattr(visitor, "visitGateApplication"):
if "measure" in str(self.children[0]).lower():
return visitor.visitMeasurementApplication(self)
return visitor.visitGateApplication(self)
else:
return visitor.visitChildren(self)
Expand Down
7 changes: 7 additions & 0 deletions opensquirrel/parsing/antlr/qubit_range_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ def visitGateApplication(self, ctx):
if len(qubit_argument_sizes) > 0 and not all(s == qubit_argument_sizes[0] for s in qubit_argument_sizes):
raise Exception("Invalid gate call with qubit arguments of different sizes")

def visitMeasurementApplication(self, ctx):
visited_args = (self.visit(arg) for arg in ctx.expr())
qubit_argument_sizes = [qubit_range_size for qubit_range_size in visited_args if qubit_range_size is not None]

if len(qubit_argument_sizes) > 0 and not all(s == qubit_argument_sizes[0] for s in qubit_argument_sizes):
raise Exception("Invalid gate call with qubit arguments of different sizes")

def visitQubit(self, ctx):
qubit_index = int(str(ctx.INT()))
if qubit_index >= self.number_of_qubits:
Expand Down
46 changes: 42 additions & 4 deletions opensquirrel/parsing/antlr/squirrel_ir_creator.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
import inspect

from opensquirrel.default_gates import default_gate_aliases, default_gate_set
from opensquirrel.gate_library import GateLibrary
from opensquirrel.default_measurements import default_measurement_aliases, default_measurement_set
from opensquirrel.operation_library import GateLibrary, MeasurementLibrary
from opensquirrel.parsing.antlr.generated import CQasm3Visitor
from opensquirrel.squirrel_ir import Float, Int, Qubit, SquirrelIR
from opensquirrel.squirrel_ir import Float, Int, Qubit, Bit, SquirrelIR


class SquirrelIRCreator(GateLibrary, CQasm3Visitor.CQasm3Visitor):
class SquirrelIRCreator(GateLibrary, MeasurementLibrary, CQasm3Visitor.CQasm3Visitor):
"""
This class creates a SquirrelIR object from an ANTLR parse tree.
It is an instance of the ANTLR abstract syntax tree visitor class.
Therefore, method names are fixed and based on rule names in the Grammar .g4 file.
"""

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

def visitProg(self, ctx):
Expand Down Expand Up @@ -46,20 +54,50 @@ def visitGateApplication(self, ctx):
for individual_args in zip(*expanded_args):
self.squirrel_ir.add_gate(generator_f(*individual_args))

def visitMeasurementApplication(self, ctx):
measurement_name = str(ctx.ID())

generator_f = MeasurementLibrary.get_measurement_f(self, measurement_name)
parameters = inspect.signature(generator_f).parameters

number_of_operands = next(
len(self.visit(ctx.expr(i))) for i, par in enumerate(parameters.values()) if par.annotation == Qubit
)

# The below is for handling e.g. `cr q[1:3], q[5:7], 1.23`
expanded_args = [
self.visit(ctx.expr(i)) if par.annotation == Qubit else [self.visit(ctx.expr(i))] * number_of_operands
for i, par in enumerate(parameters.values())
]

for individual_args in zip(*expanded_args):
self.squirrel_ir.add_measurement(generator_f(*individual_args))

def visitQubitRegisterDeclaration(self, ctx):
return int(str(ctx.INT())), str(ctx.ID())

def visitQubit(self, ctx):
return [Qubit(int(str(ctx.INT())))]

def visitBit(self, ctx):
return [Bit(int(str(ctx.INT())))]

def visitQubits(self, ctx):
return list(map(Qubit, map(int, map(str, ctx.INT()))))

def visitBits(self, ctx):
return list(map(Bit, map(int, map(str, ctx.INT()))))

def visitQubitRange(self, ctx):
first_qubit_index = int(str(ctx.INT(0)))
last_qubit_index = int(str(ctx.INT(1)))
return list(map(Qubit, range(first_qubit_index, last_qubit_index + 1)))

def visitBitRange(self, ctx):
first_bit_index = int(str(ctx.INT(0)))
last_bit_index = int(str(ctx.INT(1)))
return list(map(Bit, range(first_bit_index, last_bit_index + 1)))

def visitFloatLiteral(self, ctx):
return Float(float(str(ctx.FLOAT())))

Expand Down
23 changes: 18 additions & 5 deletions opensquirrel/parsing/antlr/squirrel_ir_from_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ def antlr_tree_from_string(s: str):
return parser.prog()


def type_check_antlr_tree(tree, gate_set: list, gate_aliases: dict):
type_checker = TypeChecker(gate_set, gate_aliases)
def type_check_antlr_tree(tree, gate_set, gate_aliases, measurement_set, measurement_aliases):
type_checker = TypeChecker(gate_set, gate_aliases, measurement_set, measurement_aliases)
type_checker.visit(tree) # FIXME: return error instead of throwing?


Expand All @@ -33,7 +33,7 @@ def check_qubit_ranges_of_antlr_tree(tree):
qubit_range_checker.visit(tree) # FIXME: return error instead of throwing?


def squirrel_ir_from_string(s: str, gate_set: list, gate_aliases: dict) -> SquirrelIR:
def squirrel_ir_from_string(s: str, gate_set, gate_aliases, measurement_set, measurement_aliases):
"""
ANTLR parsing entrypoint.
Performs type checking based on provided gate semantics and check that the qubit indices are valid.
Expand All @@ -42,16 +42,29 @@ def squirrel_ir_from_string(s: str, gate_set: list, gate_aliases: dict) -> Squir
Args:
gate_set: The set of supported gate semantics.
gate_aliases: Dictionary mapping extra gate names to their semantic.
measurement_set: The set of supported measurement semantics.
measurement_aliases: Dictionary mapping extra measurement names to their semantic.

Returns:
A corresponding SquirrelIR object. Throws in case of parsing error.
"""
tree = antlr_tree_from_string(s)

type_check_antlr_tree(tree, gate_set=gate_set, gate_aliases=gate_aliases)
type_check_antlr_tree(
tree,
gate_set=gate_set,
gate_aliases=gate_aliases,
measurement_set=measurement_set,
measurement_aliases=measurement_aliases,
)

check_qubit_ranges_of_antlr_tree(tree)

squirrel_ir_creator = SquirrelIRCreator(gate_set=gate_set, gate_aliases=gate_aliases)
squirrel_ir_creator = SquirrelIRCreator(
gate_set=gate_set,
gate_aliases=gate_aliases,
measurement_set=measurement_set,
measurement_aliases=measurement_aliases,
)

return squirrel_ir_creator.visit(tree)
Loading