diff --git a/qupsy/language.py b/qupsy/language.py index 149da6a..69d32aa 100644 --- a/qupsy/language.py +++ b/qupsy/language.py @@ -1,11 +1,13 @@ from __future__ import annotations from abc import ABC, abstractmethod +from dataclasses import dataclass from textwrap import indent TAB = " " +@dataclass class Aexp(ABC): @abstractmethod @@ -44,6 +46,7 @@ def copy(self) -> Aexp: return self.__class__(*[child.copy() for child in self.children]) +@dataclass class Integer(Aexp): def __init__(self, value: int) -> None: self.value = value @@ -66,6 +69,7 @@ def copy(self) -> Integer: return Integer(self.value) +@dataclass class Var(Aexp): def __init__(self, name: str) -> None: self.name = name @@ -88,6 +92,7 @@ def copy(self) -> Var: return Var(self.name) +@dataclass class HoleAexp(Aexp): def __str__(self) -> str: return "□_a" @@ -108,6 +113,7 @@ def terminated(self) -> bool: return False +@dataclass class Add(Aexp): a: Aexp b: Aexp @@ -120,7 +126,7 @@ def __str__(self) -> str: return f"{self.a} + {self.b}" def __repr__(self) -> str: - return f"Add({self.a!r}, {self.b!r})" + return f"Add({repr(self.a)}, {repr(self.b)})" @property def cost(self) -> int: @@ -131,6 +137,7 @@ def children(self) -> list[Aexp]: return [self.a, self.b] +@dataclass class Sub(Aexp): a: Aexp b: Aexp @@ -143,7 +150,7 @@ def __str__(self) -> str: return f"{self.a} - {self.b}" def __repr__(self) -> str: - return f"Sub({self.a!r}, {self.b!r})" + return f"Sub({repr(self.a)}, {repr(self.b)})" @property def cost(self) -> int: @@ -154,6 +161,7 @@ def children(self) -> list[Aexp]: return [self.a, self.b] +@dataclass class Mul(Aexp): a: Aexp b: Aexp @@ -166,7 +174,7 @@ def __str__(self) -> str: return f"{self.a} * {self.b}" def __repr__(self) -> str: - return f"Mul({self.a!r}, {self.b!r})" + return f"Mul({repr(self.a)}, {repr(self.b)})" @property def cost(self) -> int: @@ -177,6 +185,7 @@ def children(self) -> list[Aexp]: return [self.a, self.b] +@dataclass class Div(Aexp): a: Aexp b: Aexp @@ -189,7 +198,7 @@ def __str__(self) -> str: return f"{self.a} // {self.b}" def __repr__(self) -> str: - return f"Div({self.a!r}, {self.b!r})" + return f"Div({repr(self.a)}, {repr(self.b)})" @property def cost(self) -> int: @@ -200,6 +209,7 @@ def children(self) -> list[Aexp]: return [self.a, self.b] +@dataclass class Gate(ABC): @abstractmethod def __str__(self) -> str: @@ -237,6 +247,7 @@ def copy(self) -> Gate: return self.__class__(*[child.copy() for child in self.children]) +@dataclass class HoleGate(Gate): def __str__(self) -> str: return "□_g" @@ -257,6 +268,7 @@ def terminated(self) -> bool: return False +@dataclass class H(Gate): qreg: Aexp @@ -267,7 +279,7 @@ def __str__(self) -> str: return f"qc.append(cirq.H(qbits[{self.qreg}]))" def __repr__(self) -> str: - return f"H({self.qreg!r})" + return f"H({repr(self.qreg)})" @property def cost(self) -> int: @@ -278,6 +290,7 @@ def children(self) -> list[Aexp]: return [self.qreg] +@dataclass class X(Gate): qreg: Aexp @@ -288,7 +301,7 @@ def __str__(self) -> str: return f"qc.append(cirq.X(qbits[{self.qreg}]))" def __repr__(self) -> str: - return f"X({self.qreg!r})" + return f"X({repr(self.qreg)})" @property def cost(self) -> int: @@ -299,6 +312,7 @@ def children(self) -> list[Aexp]: return [self.qreg] +@dataclass class Ry(Gate): qreg: Aexp p: Aexp @@ -315,7 +329,7 @@ def __str__(self) -> str: return f"qc.append(cirq.Ry(rads=2*np.arccos(math.sqrt({self.p}/{self.q}))))(qbits[{self.qreg}])" def __repr__(self) -> str: - return f"Ry({self.qreg!r}, {self.p!r}, {self.q!r})" + return f"Ry({repr(self.qreg)}, {repr(self.p)}, {repr(self.q)})" @property def cost(self) -> int: @@ -326,6 +340,7 @@ def children(self) -> list[Aexp]: return [self.qreg, self.p, self.q] +@dataclass class CX(Gate): qreg1: Aexp qreg2: Aexp @@ -338,7 +353,7 @@ def __str__(self) -> str: return f"qc.append(cirq.CX(qbits[{self.qreg1}], qbits[{self.qreg2}]))" def __repr__(self) -> str: - return f"CX({self.qreg1!r}, {self.qreg2!r})" + return f"CX({repr(self.qreg1)}, {repr(self.qreg2)})" @property def cost(self) -> int: @@ -349,6 +364,7 @@ def children(self) -> list[Aexp]: return [self.qreg1, self.qreg2] +@dataclass class CRy(Gate): qreg1: Aexp qreg2: Aexp @@ -371,7 +387,7 @@ def __str__(self) -> str: return f"qc.append(cirq.Ry(rads=2*np.arccos(math.sqrt({self.p}/{self.q}))).controlled(num_controls=1)(qbits[{self.qreg1}], qbits[{self.qreg2}])" def __repr__(self) -> str: - return f"CRy({self.qreg1!r}, {self.qreg2!r}, {self.p!r}, {self.q!r})" + return f"CRy({repr(self.qreg1)}, {repr(self.qreg2)}, {repr(self.p)}, {repr(self.q)})" @property def cost(self) -> int: @@ -382,6 +398,7 @@ def children(self) -> list[Aexp]: return [self.qreg1, self.qreg2, self.p, self.q] +@dataclass class Cmd(ABC): @abstractmethod @@ -420,6 +437,7 @@ def copy(self) -> Cmd: return self.__class__(*[child.copy() for child in self.children]) +@dataclass class HoleCmd(Cmd): def __str__(self) -> str: return "□_c" @@ -440,6 +458,7 @@ def terminated(self) -> bool: return False +@dataclass class SeqCmd(Cmd): pre: Cmd post: Cmd @@ -463,6 +482,7 @@ def children(self) -> list[Cmd | Gate | Aexp]: return [self.pre, self.post] +@dataclass class ForCmd(Cmd): var: str start: Aexp @@ -482,10 +502,10 @@ def __init__( self.body = body or HoleCmd() def __str__(self) -> str: - return f"for {self.var} in range({self.start}{self.end}):\n{indent(str(self.body), TAB)}" + return f"for {self.var} in range({self.start},{self.end}):\n{indent(str(self.body), TAB)}" def __repr__(self) -> str: - return f"For({self.var!r}, {self.start!r}, {self.end!r}, {self.body!r})" + return f"For({repr(self.var)}, {repr(self.start)}, {repr(self.end)}, {repr(self.body)})" def copy(self) -> ForCmd: return ForCmd(self.var, self.start.copy(), self.end.copy(), self.body.copy()) @@ -499,6 +519,7 @@ def children(self) -> list[Cmd | Gate | Aexp]: return [self.start, self.end, self.body] +@dataclass class GateCmd(Cmd): gate: Gate @@ -509,7 +530,7 @@ def __str__(self) -> str: return str(self.gate) def __repr__(self) -> str: - return f"{self.gate!r}" + return f"{repr(self.gate)}" @property def cost(self) -> int: @@ -520,6 +541,7 @@ def children(self) -> list[Cmd | Gate | Aexp]: return [self.gate] +@dataclass class Pgm: n: str body: Cmd @@ -529,7 +551,10 @@ def __init__(self, n: str, body: Cmd | None = None) -> None: self.body = body or HoleCmd() def __str__(self) -> str: - return str(self.body) + qreg = "qbits = cirq.LineQubit.range(n)" + circuit = "qc = cirq.Circuit()" + ret = "return qc" + return f"import cirq, numpy as np\ndef pgm({self.n}):\n{indent(qreg, TAB)}\n{indent(circuit, TAB)}\n{indent(str(self.body), TAB)}\n{indent(ret, TAB)}" def __repr__(self) -> str: return f"Pgm({repr(self.body)})" diff --git a/tests/test_language.py b/tests/test_language.py index ae9a52e..1ed526c 100644 --- a/tests/test_language.py +++ b/tests/test_language.py @@ -6,11 +6,6 @@ def test_pgm_create_with_empty_body(): assert isinstance(pgm.body, HoleCmd) -def test_pgm_to_str(): - pgm = Pgm("n") - assert str(pgm) == str(pgm.body) - - def test_pgm_cost(): pgm = Pgm("n") assert pgm.cost == pgm.body.cost diff --git a/tests/test_transition.py b/tests/test_transition.py index fc2a4eb..ca0d70b 100644 --- a/tests/test_transition.py +++ b/tests/test_transition.py @@ -95,3 +95,63 @@ def test_next_aexp(): aexp_types.remove(type(pgm.body.gate.qreg2.a)) assert isinstance(pgm.body.gate.qreg2.b, HoleAexp) assert len(aexp_types) == 3 + + +def test_ghz_next(): + pgm0 = Pgm("n") + pgm1 = Pgm("n", SeqCmd()) + pgm2 = Pgm("n", SeqCmd(GateCmd())) + pgm3 = Pgm("n", SeqCmd(GateCmd(H()))) + pgm4 = Pgm("n", SeqCmd(GateCmd(H(Integer(0))))) + pgm5 = Pgm("n", SeqCmd(GateCmd(H(Integer(0))), ForCmd("i0"))) + pgm6 = Pgm("n", SeqCmd(GateCmd(H(Integer(0))), ForCmd("i0", Integer(1)))) + pgm7 = Pgm("n", SeqCmd(GateCmd(H(Integer(0))), ForCmd("i0", Integer(1), Var("n")))) + pgm8 = Pgm( + "n", + SeqCmd(GateCmd(H(Integer(0))), ForCmd("i0", Integer(1), Var("n"), GateCmd())), + ) + pgm9 = Pgm( + "n", + SeqCmd( + GateCmd(H(Integer(0))), + ForCmd("i0", Integer(1), Var("n"), GateCmd(CX())), + ), + ) + pgm10 = Pgm( + "n", + SeqCmd( + GateCmd(H(Integer(0))), + ForCmd("i0", Integer(1), Var("n"), GateCmd(CX(Integer(0)))), + ), + ) + pgm11 = Pgm( + "n", + SeqCmd( + GateCmd(H(Integer(0))), + ForCmd("i0", Integer(1), Var("n"), GateCmd(CX(Integer(0), Var("i0")))), + ), + ) + assert pgm1 in next(pgm0) + assert pgm2 in next(pgm1) + assert pgm3 in next(pgm2) + assert pgm4 in next(pgm3) + assert pgm5 in next(pgm4) + assert pgm6 in next(pgm5) + assert pgm7 in next(pgm6) + assert pgm8 in next(pgm7) + assert pgm9 in next(pgm8) + assert pgm10 in next(pgm9) + assert pgm11 in next(pgm10) + assert ( + str(pgm11) + == """ +import cirq, numpy as np +def pgm(n): + qbits = cirq.LineQubit.range(n) + qc = cirq.Circuit() + qc.append(cirq.H(qbits[0])) + for i0 in range(1,n): + qc.append(cirq.CX(qbits[0], qbits[i0])) + return qc + """.strip() + )