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-115] PEP8 naming #369

Merged
merged 6 commits into from
Nov 7, 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
14 changes: 12 additions & 2 deletions opensquirrel/circuit_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@
from opensquirrel.default_measures import default_measure_set
from opensquirrel.default_resets import default_reset_set
from opensquirrel.instruction_library import GateLibrary, MeasureLibrary, ResetLibrary
from opensquirrel.ir import ANNOTATIONS_TO_TYPE_MAP, IR, Comment, Gate, Measure, Qubit, QubitLike, Reset
from opensquirrel.ir import (
ANNOTATIONS_TO_TYPE_MAP,
IR,
Comment,
Gate,
Measure,
Qubit,
QubitLike,
Reset,
)
from opensquirrel.register_manager import BitRegister, QubitRegister, RegisterManager


Expand Down Expand Up @@ -135,7 +144,8 @@ def _check_generator_f_args(
try:
is_incorrect_type = not isinstance(args[i], expected_type) # type: ignore
except TypeError:
# expected type is probably a Union, which works differently in python39
# expected type is probably a Union, which works differently in
# python39
is_incorrect_type = not isinstance(args[i], expected_type.__args__) # type: ignore

if is_incorrect_type:
Expand Down
19 changes: 14 additions & 5 deletions opensquirrel/decomposer/aba_decomposer.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,28 +70,37 @@ def get_decomposition_angles(self, alpha: float, axis: AxisLike) -> tuple[float,
p = math.pi
theta2 = 2 * math.acos(a_axis_value)
if abs(a_axis_value - 1) < ATOL or abs(a_axis_value + 1) < ATOL:
m = p # This can be anything, but setting m = p means theta3 == 0, which is better for gate count.
# This can be anything, but setting m = p means theta3 ==
# 0, which is better for gate count.
m = p
else:
m = 2 * math.acos(
round(b_axis_value / math.sqrt(1 - a_axis_value**2), abs(math.floor(math.log10(ATOL)))),
round(
b_axis_value / math.sqrt(1 - a_axis_value**2),
abs(math.floor(math.log10(ATOL))),
)
)

else:
p = 2 * math.atan2(a_axis_value * math.sin(alpha / 2), math.cos(alpha / 2))
acos_argument = math.cos(alpha / 2) * math.sqrt(1 + (a_axis_value * math.tan(alpha / 2)) ** 2)

# This fixes float approximations like 1.0000000000002, which acos does not like.
# This fixes float approximations like 1.0000000000002, which acos
# does not like.
acos_argument = max(min(acos_argument, 1.0), -1.0)

theta2 = 2 * math.acos(acos_argument)
theta2 = math.copysign(theta2, alpha)

if abs(math.sin(theta2 / 2)) < ATOL:
m = p # This can be anything, but setting m = p means theta3 == 0, which is better for gate count.
# This can be anything, but setting m = p means theta3 == 0,
# which is better for gate count.
m = p
else:
acos_argument = float(b_axis_value) * math.sin(alpha / 2) / math.sin(theta2 / 2)

# This fixes float approximations like 1.0000000000002, which acos does not like.
# This fixes float approximations like 1.0000000000002, which
# acos does not like.
acos_argument = max(min(acos_argument, 1.0), -1.0)
m = 2 * math.acos(acos_argument)
if math.pi - abs(m) > ATOL:
Expand Down
34 changes: 25 additions & 9 deletions opensquirrel/decomposer/cnot_decomposer.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,26 @@ def decompose(self, g: Gate) -> list[Gate]:
# This gives us an ABC decomposition (U = AXBXC, ABC = I) of the target gate.
# See https://threeplusone.com/pubs/on_gates.pdf

# Try special case first, see https://arxiv.org/pdf/quant-ph/9503016.pdf lemma 5.5
# Try special case first, see
# https://arxiv.org/pdf/quant-ph/9503016.pdf lemma 5.5
controlled_rotation_times_x = general_merger.compose_bloch_sphere_rotations(X(target_qubit), g.target_gate)
theta0_with_x, theta1_with_x, theta2_with_x = ZYZDecomposer().get_decomposition_angles(
controlled_rotation_times_x.angle,
controlled_rotation_times_x.axis,
(
theta0_with_x,
theta1_with_x,
theta2_with_x,
) = ZYZDecomposer().get_decomposition_angles(
controlled_rotation_times_x.angle, controlled_rotation_times_x.axis
)
if abs((theta0_with_x - theta2_with_x) % (2 * math.pi)) < ATOL:
# The decomposition can use a single CNOT according to the lemma.
A = [Ry(target_qubit, Float(-theta1_with_x / 2)), Rz(target_qubit, Float(-theta2_with_x))]
B = [Rz(target_qubit, Float(theta2_with_x)), Ry(target_qubit, Float(theta1_with_x / 2))]
A = [ # noqa: N806
Ry(target_qubit, Float(-theta1_with_x / 2)),
Rz(target_qubit, Float(-theta2_with_x)),
]
B = [ # noqa: N806
Rz(target_qubit, Float(theta2_with_x)),
Ry(target_qubit, Float(theta1_with_x / 2)),
]

return filter_out_identities(
[
Expand All @@ -59,9 +69,15 @@ def decompose(self, g: Gate) -> list[Gate]:

theta0, theta1, theta2 = ZYZDecomposer().get_decomposition_angles(g.target_gate.angle, g.target_gate.axis)

A = [Ry(target_qubit, Float(theta1 / 2)), Rz(target_qubit, Float(theta2))]
B = [Rz(target_qubit, Float(-(theta0 + theta2) / 2)), Ry(target_qubit, Float(-theta1 / 2))]
C = [Rz(target_qubit, Float((theta0 - theta2) / 2))]
A = [ # noqa: N806
Ry(target_qubit, Float(theta1 / 2)),
Rz(target_qubit, Float(theta2)),
]
B = [ # noqa: N806
Rz(target_qubit, Float(-(theta0 + theta2) / 2)),
Ry(target_qubit, Float(-theta1 / 2)),
]
C = [Rz(target_qubit, Float((theta0 - theta2) / 2))] # noqa: N806

return filter_out_identities(
[
Expand Down
6 changes: 5 additions & 1 deletion opensquirrel/decomposer/general_decomposer.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ def decompose(ir: IR, decomposer: Decomposer) -> None:


class _GenericReplacer(Decomposer):
def __init__(self, gate_generator: Callable[..., Gate], replacement_function: Callable[..., list[Gate]]) -> None:
def __init__(
self,
gate_generator: Callable[..., Gate],
replacement_function: Callable[..., list[Gate]],
) -> None:
self.gate_generator = gate_generator
self.replacement_function = replacement_function

Expand Down
59 changes: 37 additions & 22 deletions opensquirrel/default_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,111 +4,126 @@
from collections.abc import Callable
from typing import SupportsInt

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


@named_gate
def I(q: QubitLike) -> BlochSphereRotation: # noqa: E743
def I(q: QubitLike) -> BlochSphereRotation: # noqa: E743, N802
return BlochSphereRotation.identity(q)


@named_gate
def H(q: QubitLike) -> BlochSphereRotation:
def H(q: QubitLike) -> BlochSphereRotation: # noqa: N802
return BlochSphereRotation(qubit=q, axis=(1, 0, 1), angle=math.pi, phase=math.pi / 2)


@named_gate
def X(q: QubitLike) -> BlochSphereRotation:
def X(q: QubitLike) -> BlochSphereRotation: # noqa: N802
return BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=math.pi, phase=math.pi / 2)


@named_gate
def X90(q: QubitLike) -> BlochSphereRotation:
def X90(q: QubitLike) -> BlochSphereRotation: # noqa: N802
return BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=math.pi / 2, phase=0)


@named_gate
def mX90(q: QubitLike) -> BlochSphereRotation:
def mX90(q: QubitLike) -> BlochSphereRotation: # noqa: N802
return BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=-math.pi / 2, phase=-0)


@named_gate
def Y(q: QubitLike) -> BlochSphereRotation:
def Y(q: QubitLike) -> BlochSphereRotation: # noqa: N802
return BlochSphereRotation(qubit=q, axis=(0, 1, 0), angle=math.pi, phase=math.pi / 2)


@named_gate
def Y90(q: QubitLike) -> BlochSphereRotation:
def Y90(q: QubitLike) -> BlochSphereRotation: # noqa: N802
return BlochSphereRotation(qubit=q, axis=(0, 1, 0), angle=math.pi / 2, phase=0)


@named_gate
def mY90(q: QubitLike) -> BlochSphereRotation:
def mY90(q: QubitLike) -> BlochSphereRotation: # noqa: N802
return BlochSphereRotation(qubit=q, axis=(0, 1, 0), angle=-math.pi / 2, phase=0)


@named_gate
def Z(q: QubitLike) -> BlochSphereRotation:
def Z(q: QubitLike) -> BlochSphereRotation: # noqa: N802
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=math.pi, phase=math.pi / 2)


@named_gate
def S(q: QubitLike) -> BlochSphereRotation:
def S(q: QubitLike) -> BlochSphereRotation: # noqa: N802
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=math.pi / 2, phase=0)


@named_gate
def Sdag(q: QubitLike) -> BlochSphereRotation:
def Sdag(q: QubitLike) -> BlochSphereRotation: # noqa: N802
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=-math.pi / 2, phase=0)


@named_gate
def T(q: QubitLike) -> BlochSphereRotation:
def T(q: QubitLike) -> BlochSphereRotation: # noqa: N802
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=math.pi / 4, phase=0)


@named_gate
def Tdag(q: QubitLike) -> BlochSphereRotation:
def Tdag(q: QubitLike) -> BlochSphereRotation: # noqa: N802
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=-math.pi / 4, phase=0)


@named_gate
def Rx(q: QubitLike, theta: Float) -> BlochSphereRotation:
def Rx(q: QubitLike, theta: Float) -> BlochSphereRotation: # noqa: N802
return BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=theta.value, phase=0)


@named_gate
def Ry(q: QubitLike, theta: Float) -> BlochSphereRotation:
def Ry(q: QubitLike, theta: Float) -> BlochSphereRotation: # noqa: N802
return BlochSphereRotation(qubit=q, axis=(0, 1, 0), angle=theta.value, phase=0)


@named_gate
def Rz(q: QubitLike, theta: Float) -> BlochSphereRotation:
def Rz(q: QubitLike, theta: Float) -> BlochSphereRotation: # noqa: N802
return BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=theta.value, phase=0)


@named_gate
def CNOT(control: QubitLike, target: QubitLike) -> ControlledGate:
def CNOT(control: QubitLike, target: QubitLike) -> ControlledGate: # noqa: N802
return ControlledGate(control, X(target))


@named_gate
def CZ(control: QubitLike, target: QubitLike) -> ControlledGate:
def CZ(control: QubitLike, target: QubitLike) -> ControlledGate: # noqa: N802
return ControlledGate(control, Z(target))


@named_gate
def CR(control: QubitLike, target: QubitLike, theta: Float) -> ControlledGate:
def CR( # noqa: N802
control: QubitLike, target: QubitLike, theta: Float
) -> ControlledGate:
return ControlledGate(
control,
BlochSphereRotation(qubit=target, axis=(0, 0, 1), angle=theta.value, phase=theta.value / 2),
)


@named_gate
def CRk(control: QubitLike, target: QubitLike, k: SupportsInt) -> ControlledGate:
def CRk( # noqa: N802
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))
return ControlledGate(
control,
BlochSphereRotation(qubit=target, axis=(0, 0, 1), angle=theta, phase=theta / 2),
)


default_bloch_sphere_rotations_without_params: list[Callable[[QubitLike], BlochSphereRotation]]
Expand Down
3 changes: 2 additions & 1 deletion opensquirrel/exporter/cqasmv1_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,5 @@ def export(circuit: Circuit) -> str:

circuit.ir.accept(cqasmv1_creator)

return cqasmv1_creator.cqasmv1_string.rstrip() + "\n" # remove all trailing lines and leave only one
# remove all trailing lines and leave only one
return cqasmv1_creator.cqasmv1_string.rstrip() + "\n"
17 changes: 14 additions & 3 deletions opensquirrel/exporter/quantify_scheduler_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@
from opensquirrel.common import ATOL
from opensquirrel.default_gates import X, Z
from opensquirrel.exceptions import ExporterError, UnsupportedGateError
from opensquirrel.ir import BlochSphereRotation, ControlledGate, IRVisitor, MatrixGate, Measure, Qubit, Reset
from opensquirrel.ir import (
BlochSphereRotation,
ControlledGate,
IRVisitor,
MatrixGate,
Measure,
Qubit,
Reset,
)

try:
import quantify_scheduler
Expand Down Expand Up @@ -40,7 +48,8 @@ def visit_bloch_sphere_rotation(self, g: BlochSphereRotation) -> None:
# Note that when adding a rotation gate to the Quantify-scheduler Schedule,
# there exists an ambiguity with how Quantify-scheduler will store an angle of 180 degrees.
# Depending on the system the angle may be stored as either 180 or -180 degrees.
# This ambiguity has no physical consequences, but may cause the exporter test fail.
# This ambiguity has no physical consequences, but may cause the
# exporter test to fail.
g_qubit = Qubit(g.qubit)
if abs(g.axis[2]) < ATOL:
# Rxy rotation.
Expand Down Expand Up @@ -110,7 +119,9 @@ def visit_reset(self, g: Reset) -> Any:
self.schedule.add(quantify_scheduler_gates.Reset(self._get_qubit_string(g.qubit)))


def export(circuit: Circuit) -> tuple[quantify_scheduler.Schedule, list[tuple[Any, Any]]]:
def export(
circuit: Circuit,
) -> tuple[quantify_scheduler.Schedule, list[tuple[Any, Any]]]:
if "quantify_scheduler" not in globals():

class QuantifySchedulerNotInstalled:
Expand Down
20 changes: 16 additions & 4 deletions opensquirrel/ir.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,18 @@
import numpy as np
from numpy.typing import ArrayLike, DTypeLike, NDArray

from opensquirrel.common import ATOL, are_matrices_equivalent_up_to_global_phase, normalize_angle
from opensquirrel.common import (
ATOL,
are_matrices_equivalent_up_to_global_phase,
normalize_angle,
)

REPR_DECIMALS = 5


def repr_round(
value: float | Axis | NDArray[np.complex64 | np.complex128], decimals: int = REPR_DECIMALS
value: float | Axis | NDArray[np.complex64 | np.complex128],
decimals: int = REPR_DECIMALS,
) -> float | NDArray[np.complex64 | np.complex128]:
return np.round(value, decimals)

Expand Down Expand Up @@ -245,7 +250,13 @@ def _normalize_axis(axis: NDArray[np.float64]) -> NDArray[np.float64]:
"""
return axis / np.linalg.norm(axis)

def __getitem__(self, index: int, /) -> np.float64: # type:ignore[override]
@overload
def __getitem__(self, i: int, /) -> np.float64: ...

@overload
def __getitem__(self, s: slice, /) -> list[np.float64]: ...

def __getitem__(self, index: int | slice, /) -> np.float64 | list[np.float64]:
"""Get the item at `index`."""
return cast(np.float64, self.value[index])

Expand Down Expand Up @@ -362,7 +373,8 @@ def __init__(
*args: Any,
**kwargs: Any,
) -> None:
# Note: two gates are considered equal even when their generators/arguments are different.
# Note: two gates are considered equal even when their
# generators/arguments are different.
self.generator = generator
self.arguments = arguments

Expand Down
6 changes: 4 additions & 2 deletions opensquirrel/merger/general_merger.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ def compose_bloch_sphere_rotations(a: BlochSphereRotation, b: BlochSphereRotatio
raise ValueError(msg)

acos_argument = cos(a.angle / 2) * cos(b.angle / 2) - sin(a.angle / 2) * sin(b.angle / 2) * np.dot(a.axis, b.axis)
# This fixes float approximations like 1.0000000000002 which acos doesn't like.
# This fixes float approximations like 1.0000000000002 which acos doesn't
# like.
acos_argument = max(min(acos_argument, 1.0), -1.0)

combined_angle = 2 * acos(acos_argument)
Expand Down Expand Up @@ -110,7 +111,8 @@ def merge_single_qubit_gates(circuit: Circuit) -> None:
del ir.statements[statement_index]
continue

# Skip controlled-gates, measure, reset, and reset accumulator for their qubit operands
# Skip controlled-gates, measure, reset, and reset accumulator for
# their qubit operands
for qubit_operand in statement.get_qubit_operands(): # type: ignore
if not accumulators_per_qubit[qubit_operand].is_identity():
ir.statements.insert(statement_index, accumulators_per_qubit[qubit_operand])
Expand Down
Loading