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

[CQT-235] Add support for gate modifiers #367

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
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
40 changes: 40 additions & 0 deletions opensquirrel/default_gate_modifiers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from __future__ import annotations

from abc import ABC
from collections.abc import Callable
from opensquirrel.ir import BlochSphereRotation, ControlledGate, QubitLike
from typing import SupportsInt


class GateModifier(ABC):
pass


class InverseGateModifier(GateModifier):
def __init__(self, generator_f_gate: Callable[..., BlochSphereRotation]):
self.generator_f_gate = generator_f_gate

def __call__(self, q: QubitLike) -> BlochSphereRotation:
gate: BlochSphereRotation = self.generator_f_gate(q)
modified_angle = gate.angle * -1
return BlochSphereRotation(qubit=gate.qubit, axis=gate.axis, angle=modified_angle, phase=gate.phase)


class PowerGateModifier(GateModifier):
def __init__(self, exponent: SupportsInt, generator_f_gate: Callable[..., BlochSphereRotation]):
self.exponent = exponent
self.generator_f_gate = generator_f_gate

def __call__(self, q: QubitLike) -> BlochSphereRotation:
gate: BlochSphereRotation = self.generator_f_gate(q)
modified_angle = gate.angle * self.exponent
return BlochSphereRotation(qubit=gate.qubit, axis=gate.axis, angle=modified_angle, phase=gate.phase)


class ControlGateModifier(GateModifier):
def __init__(self, generator_f_gate: Callable[..., BlochSphereRotation]):
self.generator_f_gate = generator_f_gate

def __call__(self, control: QubitLike, target: QubitLike) -> ControlledGate:
gate: BlochSphereRotation = self.generator_f_gate(target)
return ControlledGate(control, gate)
195 changes: 144 additions & 51 deletions opensquirrel/default_gates.py
Original file line number Diff line number Diff line change
@@ -1,117 +1,211 @@
from __future__ import annotations

import math
from collections.abc import Callable
from typing import SupportsInt
from typing import Any

from opensquirrel.ir import BlochSphereRotation, ControlledGate, Float, Gate, Int, QubitLike, named_gate


class NamedGateFunctor:
def __init__(self, parameter: Any = None):
self.parameter = parameter

def __call__(self, *args: Any, **kwargs: Any) -> Any:
pass


@named_gate
def I(q: QubitLike) -> BlochSphereRotation: # noqa: E743
return BlochSphereRotation.identity(q)
class I(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, q: QubitLike) -> BlochSphereRotation: # noqa: E743
return BlochSphereRotation.identity(q)


@named_gate
def H(q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(1, 0, 1), angle=math.pi, phase=math.pi / 2)
class H(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(1, 0, 1), angle=math.pi, phase=math.pi / 2)


@named_gate
def X(q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=math.pi, phase=math.pi / 2)
class X(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=math.pi, phase=math.pi / 2)


@named_gate
def X90(q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=math.pi / 2, phase=0)
class X90(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=math.pi / 2, phase=0)


@named_gate
def mX90(q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=-math.pi / 2, phase=-0)
class mX90(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=-math.pi / 2, phase=-0)


@named_gate
def Y(q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 1, 0), angle=math.pi, phase=math.pi / 2)
class Y(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 1, 0), angle=math.pi, phase=math.pi / 2)


@named_gate
def Y90(q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 1, 0), angle=math.pi / 2, phase=0)
class Y90(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 1, 0), angle=math.pi / 2, phase=0)


@named_gate
def mY90(q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 1, 0), angle=-math.pi / 2, phase=0)
class mY90(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 1, 0), angle=-math.pi / 2, phase=0)


@named_gate
def Z(q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=math.pi, phase=math.pi / 2)
class Z(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=math.pi, phase=math.pi / 2)


@named_gate
def S(q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=math.pi / 2, phase=0)
class S(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=math.pi / 2, phase=0)


@named_gate
def Sdag(q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=-math.pi / 2, phase=0)
class Sdag:
def __init__(self, parameter: Any = None):
pass

def __call__(self, q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=-math.pi / 2, phase=0)


@named_gate
def T(q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=math.pi / 4, phase=0)
class T(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=math.pi / 4, phase=0)


@named_gate
def Tdag(q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=-math.pi / 4, phase=0)
class Tdag(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, q: QubitLike) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=-math.pi / 4, phase=0)


@named_gate
def Rx(q: QubitLike, theta: Float) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=theta.value, phase=0)
class Rx(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, q: QubitLike) -> BlochSphereRotation:
self.theta = Float(self.parameter).value
return BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=self.theta, phase=0)


@named_gate
def Ry(q: QubitLike, theta: Float) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 1, 0), angle=theta.value, phase=0)
class Ry(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, q: QubitLike) -> BlochSphereRotation:
self.theta = Float(self.parameter).value
return BlochSphereRotation(qubit=q, axis=(0, 1, 0), angle=self.theta, phase=0)


@named_gate
def Rz(q: QubitLike, theta: Float) -> BlochSphereRotation:
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=theta.value, phase=0)
class Rz(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, q: QubitLike) -> BlochSphereRotation:
self.theta = Float(self.parameter).value
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=self.theta, phase=0)


@named_gate
def CNOT(control: QubitLike, target: QubitLike) -> ControlledGate:
return ControlledGate(control, X(target))
class CNOT(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, control: QubitLike, target: QubitLike) -> ControlledGate:
return ControlledGate(control, X()(target))


@named_gate
def CZ(control: QubitLike, target: QubitLike) -> ControlledGate:
return ControlledGate(control, Z(target))
class CZ(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, control: QubitLike, target: QubitLike) -> ControlledGate:
return ControlledGate(control, Z()(target))


@named_gate
def CR(control: QubitLike, target: QubitLike, theta: Float) -> ControlledGate:
return ControlledGate(
control,
BlochSphereRotation(qubit=target, axis=(0, 0, 1), angle=theta.value, phase=theta.value / 2),
)
class CR(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, control: QubitLike, target: QubitLike) -> ControlledGate:
self.theta = Float(self.parameter).value
return ControlledGate(
control,
BlochSphereRotation(qubit=target, axis=(0, 0, 1), angle=self.theta, phase=self.theta / 2),
)


@named_gate
def CRk(control: QubitLike, target: QubitLike, k: SupportsInt) -> ControlledGate:
theta = 2 * math.pi / (2 ** Int(k).value)
return ControlledGate(control, BlochSphereRotation(qubit=target, axis=(0, 0, 1), angle=theta, phase=theta / 2))
class CRk(NamedGateFunctor):
def __init__(self, parameter: Any = None):
super().__init__(parameter)

def __call__(self, control: QubitLike, target: QubitLike) -> ControlledGate:
self.theta = 2 * math.pi / (2 ** Int(self.parameter).value)
return ControlledGate(
control,
BlochSphereRotation(qubit=target, axis=(0, 0, 1), angle=self.theta, phase=self.theta / 2)
)


default_bloch_sphere_rotations_without_params: list[Callable[[QubitLike], BlochSphereRotation]]
default_bloch_sphere_rotations_without_params: list[type[NamedGateFunctor]]
default_bloch_sphere_rotations_without_params = [
I,
H,
Expand All @@ -127,16 +221,14 @@ def CRk(control: QubitLike, target: QubitLike, k: SupportsInt) -> ControlledGate
T,
Tdag,
]
default_bloch_sphere_rotations: list[
Callable[[QubitLike], BlochSphereRotation] | Callable[[QubitLike, Float], BlochSphereRotation]
]
default_bloch_sphere_rotations: list[type[NamedGateFunctor]]
default_bloch_sphere_rotations = [
*default_bloch_sphere_rotations_without_params,
Rx,
Ry,
Rz,
]
default_gate_set: list[Callable[..., Gate]]
default_gate_set: list[type[NamedGateFunctor]]
default_gate_set = [
*default_bloch_sphere_rotations,
CNOT,
Expand All @@ -145,6 +237,7 @@ def CRk(control: QubitLike, target: QubitLike, k: SupportsInt) -> ControlledGate
CRk,
]

default_gate_aliases: dict[str, type[NamedGateFunctor]]
default_gate_aliases = {
"Hadamard": H,
"Identity": I,
Expand Down
13 changes: 7 additions & 6 deletions opensquirrel/instruction_library.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from __future__ import annotations

from collections.abc import Callable, Iterable, Mapping
from opensquirrel.default_gates import NamedGateFunctor
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from opensquirrel.ir import Gate, Measure, Reset
from opensquirrel.ir import Float, Gate, Measure, Reset


class InstructionLibrary:
Expand All @@ -14,21 +15,21 @@ class InstructionLibrary:
class GateLibrary(InstructionLibrary):
def __init__(
self,
gate_set: Iterable[Callable[..., Gate]],
gate_aliases: Mapping[str, Callable[..., Gate]],
gate_set: Iterable[NamedGateFunctor],
gate_aliases: Mapping[str, NamedGateFunctor],
) -> None:
self.gate_set = gate_set
self.gate_aliases = gate_aliases

def get_gate_f(self, gate_name: str) -> Callable[..., Gate]:
def get_gate_f(self, gate_name: str, gate_parameter: Float) -> type[NamedGateFunctor]:
try:
generator_f = next(f for f in self.gate_set if f.__name__ == gate_name)
except StopIteration as exc:
if gate_name not in self.gate_aliases:
msg = f"unknown instruction `{gate_name}`"
msg = f"unknown gate `{gate_name}`"
raise ValueError(msg) from exc
generator_f = self.gate_aliases[gate_name]
return generator_f
return generator_f(gate_parameter)


class MeasureLibrary(InstructionLibrary):
Expand Down
Loading
Loading