Skip to content

Commit

Permalink
Add Verify function (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
glorialeezero authored Nov 26, 2024
2 parents e92e17a + be2407f commit c2f038f
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 29 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "qupsy",
"image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye",
"image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye",
"features": {
"ghcr.io/devcontainers/features/common-utils:2": {
"installZsh": true,
Expand Down
5 changes: 2 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ jobs:
- '3.10'
- '3.11'
- '3.12'
- '3.13'

runs-on: ubuntu-latest

Expand Down Expand Up @@ -82,10 +81,10 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Set up Python 3.13
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.13'
python-version: '3.12'

- name: Install dependencies
run: |
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ classifiers = [
dynamic = ["version"]
dependencies = [
"rich>=13",
"numpy>=2",
"cirq>=1"
]

[project.optional-dependencies]
Expand Down
129 changes: 112 additions & 17 deletions qupsy/language.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
from dataclasses import dataclass
from textwrap import indent

import cirq
import numpy as np
from cirq import Circuit, Gate, LineQubit # type: ignore

TAB = " "


Expand Down Expand Up @@ -45,6 +49,10 @@ def terminated(self) -> bool:
def copy(self) -> Aexp:
return self.__class__(*[child.copy() for child in self.children])

@abstractmethod
def __call__(self, memory: dict[str, int]) -> int:
pass


@dataclass
class Integer(Aexp):
Expand All @@ -68,6 +76,9 @@ def children(self) -> list[Aexp]:
def copy(self) -> Integer:
return Integer(self.value)

def __call__(self, memory: dict[str, int]) -> int:
return self.value


@dataclass
class Var(Aexp):
Expand All @@ -91,6 +102,9 @@ def children(self) -> list[Aexp]:
def copy(self) -> Var:
return Var(self.name)

def __call__(self, memory: dict[str, int]) -> int:
return memory[self.name]


@dataclass
class HoleAexp(Aexp):
Expand All @@ -112,6 +126,9 @@ def children(self) -> list[Aexp]:
def terminated(self) -> bool:
return False

def __call__(self, memory: dict[str, int]) -> int:
raise ValueError("HoleAexp cannot be called")


@dataclass
class Add(Aexp):
Expand All @@ -123,7 +140,7 @@ def __init__(self, a: Aexp | None = None, b: Aexp | None = None) -> None:
self.b = b or HoleAexp()

def __str__(self) -> str:
return f"{self.a} + {self.b}"
return f"({self.a} + {self.b})"

def __repr__(self) -> str:
return f"Add({repr(self.a)}, {repr(self.b)})"
Expand All @@ -136,6 +153,9 @@ def cost(self) -> int:
def children(self) -> list[Aexp]:
return [self.a, self.b]

def __call__(self, memory: dict[str, int]) -> int:
return self.a(memory) + self.b(memory)


@dataclass
class Sub(Aexp):
Expand All @@ -147,7 +167,7 @@ def __init__(self, a: Aexp | None = None, b: Aexp | None = None) -> None:
self.b = b or HoleAexp()

def __str__(self) -> str:
return f"{self.a} - {self.b}"
return f"({self.a} - {self.b})"

def __repr__(self) -> str:
return f"Sub({repr(self.a)}, {repr(self.b)})"
Expand All @@ -160,6 +180,9 @@ def cost(self) -> int:
def children(self) -> list[Aexp]:
return [self.a, self.b]

def __call__(self, memory: dict[str, int]) -> int:
return self.a(memory) - self.b(memory)


@dataclass
class Mul(Aexp):
Expand All @@ -171,7 +194,7 @@ def __init__(self, a: Aexp | None = None, b: Aexp | None = None) -> None:
self.b = b or HoleAexp()

def __str__(self) -> str:
return f"{self.a} * {self.b}"
return f"({self.a} * {self.b})"

def __repr__(self) -> str:
return f"Mul({repr(self.a)}, {repr(self.b)})"
Expand All @@ -184,6 +207,9 @@ def cost(self) -> int:
def children(self) -> list[Aexp]:
return [self.a, self.b]

def __call__(self, memory: dict[str, int]) -> int:
return self.a(memory) * self.b(memory)


@dataclass
class Div(Aexp):
Expand All @@ -195,7 +221,7 @@ def __init__(self, a: Aexp | None = None, b: Aexp | None = None) -> None:
self.b = b or HoleAexp()

def __str__(self) -> str:
return f"{self.a} // {self.b}"
return f"({self.a} // {self.b})"

def __repr__(self) -> str:
return f"Div({repr(self.a)}, {repr(self.b)})"
Expand All @@ -208,6 +234,9 @@ def cost(self) -> int:
def children(self) -> list[Aexp]:
return [self.a, self.b]

def __call__(self, memory: dict[str, int]) -> int:
return self.a(memory) // self.b(memory)


@dataclass
class Gate(ABC):
Expand Down Expand Up @@ -246,6 +275,10 @@ def terminated(self) -> bool:
def copy(self) -> Gate:
return self.__class__(*[child.copy() for child in self.children])

@abstractmethod
def __call__(self, qbits: list[LineQubit], memory: dict[str, int]) -> Gate:
pass


@dataclass
class HoleGate(Gate):
Expand All @@ -267,6 +300,9 @@ def children(self) -> list[Aexp]:
def terminated(self) -> bool:
return False

def __call__(self, qbits: list[LineQubit], memory: dict[str, int]) -> Gate:
raise ValueError("HoleGate cannot be called")


@dataclass
class H(Gate):
Expand All @@ -276,7 +312,7 @@ def __init__(self, qreg: Aexp | None = None) -> None:
self.qreg = qreg or HoleAexp()

def __str__(self) -> str:
return f"qc.append(cirq.H(qbits[{self.qreg}]))"
return f"H({self.qreg})"

def __repr__(self) -> str:
return f"H({repr(self.qreg)})"
Expand All @@ -289,6 +325,10 @@ def cost(self) -> int:
def children(self) -> list[Aexp]:
return [self.qreg]

def __call__(self, qbits: list[LineQubit], memory: dict[str, int]) -> Gate:
idx = self.qreg(memory)
return cirq.H(qbits[idx]) # type: ignore


@dataclass
class X(Gate):
Expand All @@ -298,7 +338,7 @@ def __init__(self, qreg: Aexp | None = None) -> None:
self.qreg = qreg or HoleAexp()

def __str__(self) -> str:
return f"qc.append(cirq.X(qbits[{self.qreg}]))"
return f"X({self.qreg})"

def __repr__(self) -> str:
return f"X({repr(self.qreg)})"
Expand All @@ -311,12 +351,16 @@ def cost(self) -> int:
def children(self) -> list[Aexp]:
return [self.qreg]

def __call__(self, qbits: list[LineQubit], memory: dict[str, int]) -> Gate:
idx = self.qreg(memory)
return cirq.X(qbits[idx]) # type: ignore


@dataclass
class Ry(Gate):
qreg: Aexp
p: Aexp
q: Aexp
p: Aexp # TODO
q: Aexp # TODO

def __init__(
self, qreg: Aexp | None = None, p: Aexp | None = None, q: Aexp | None = None
Expand All @@ -326,7 +370,7 @@ def __init__(
self.q = q or HoleAexp()

def __str__(self) -> str:
return f"qc.append(cirq.Ry(rads=2*np.arccos(math.sqrt({self.p}/{self.q}))))(qbits[{self.qreg}])"
return f"Ry({self.qreg}, {self.p}, {self.q})"

def __repr__(self) -> str:
return f"Ry({repr(self.qreg)}, {repr(self.p)}, {repr(self.q)})"
Expand All @@ -339,6 +383,10 @@ def cost(self) -> int:
def children(self) -> list[Aexp]:
return [self.qreg, self.p, self.q]

def __call__(self, qbits: list[LineQubit], memory: dict[str, int]) -> Gate:
idx = self.qreg(memory)
return cirq.Ry(rads=2 * np.arccos(np.sqrt(self.p(memory) / self.q(memory))))(qbits[idx]) # type: ignore


@dataclass
class CX(Gate):
Expand All @@ -350,7 +398,7 @@ def __init__(self, qreg1: Aexp | None = None, qreg2: Aexp | None = None) -> None
self.qreg2 = qreg2 or HoleAexp()

def __str__(self) -> str:
return f"qc.append(cirq.CX(qbits[{self.qreg1}], qbits[{self.qreg2}]))"
return f"CX({self.qreg1}, {self.qreg2})"

def __repr__(self) -> str:
return f"CX({repr(self.qreg1)}, {repr(self.qreg2)})"
Expand All @@ -363,6 +411,11 @@ def cost(self) -> int:
def children(self) -> list[Aexp]:
return [self.qreg1, self.qreg2]

def __call__(self, qbits: list[LineQubit], memory: dict[str, int]) -> Gate:
idx1 = self.qreg1(memory)
idx2 = self.qreg2(memory)
return cirq.CX(qbits[idx1], qbits[idx2]) # type: ignore


@dataclass
class CRy(Gate):
Expand All @@ -380,11 +433,11 @@ def __init__(
) -> None:
self.qreg1 = qreg1 or HoleAexp()
self.qreg2 = qreg2 or HoleAexp()
self.p = p or HoleAexp()
self.q = q or HoleAexp()
self.p = p or HoleAexp() # TODO
self.q = q or HoleAexp() # TODO

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}])"
return f"CRy({self.qreg1}, {self.qreg2}, {self.p}, {self.q})"

def __repr__(self) -> str:
return f"CRy({repr(self.qreg1)}, {repr(self.qreg2)}, {repr(self.p)}, {repr(self.q)})"
Expand All @@ -397,6 +450,11 @@ def cost(self) -> int:
def children(self) -> list[Aexp]:
return [self.qreg1, self.qreg2, self.p, self.q]

def __call__(self, qbits: list[LineQubit], memory: dict[str, int]) -> Gate:
idx1 = self.qreg1(memory)
idx2 = self.qreg2(memory)
return cirq.Ry(rads=2 * np.arccos(np.sqrt(self.p(memory) / self.q(memory)))).controlled(num_controls=1)(qbits[idx1], qbits[idx2]) # type: ignore


@dataclass
class Cmd(ABC):
Expand Down Expand Up @@ -436,6 +494,12 @@ def terminated(self) -> bool:
def copy(self) -> Cmd:
return self.__class__(*[child.copy() for child in self.children])

@abstractmethod
def __call__(
self, qc: Circuit, qbits: list[LineQubit], memory: dict[str, int]
) -> None:
pass


@dataclass
class HoleCmd(Cmd):
Expand All @@ -457,6 +521,11 @@ def children(self) -> list[Cmd | Gate | Aexp]:
def terminated(self) -> bool:
return False

def __call__(
self, qc: Circuit, qbits: list[LineQubit], memory: dict[str, int]
) -> None:
raise ValueError("HoleCmd cannot be called")


@dataclass
class SeqCmd(Cmd):
Expand All @@ -481,6 +550,12 @@ def cost(self) -> int:
def children(self) -> list[Cmd | Gate | Aexp]:
return [self.pre, self.post]

def __call__(
self, qc: Circuit, qbits: list[LineQubit], memory: dict[str, int]
) -> None:
self.pre(qc, qbits, memory)
self.post(qc, qbits, memory)


@dataclass
class ForCmd(Cmd):
Expand Down Expand Up @@ -518,6 +593,16 @@ def cost(self) -> int:
def children(self) -> list[Cmd | Gate | Aexp]:
return [self.start, self.end, self.body]

def __call__(
self, qc: Circuit, qbits: list[LineQubit], memory: dict[str, int]
) -> None:
start = self.start(memory)
end = self.end(memory)
for i in range(start, end):
memory[self.var] = i
self.body(qc, qbits, memory)
del memory[self.var]


@dataclass
class GateCmd(Cmd):
Expand All @@ -540,6 +625,12 @@ def cost(self) -> int:
def children(self) -> list[Cmd | Gate | Aexp]:
return [self.gate]

def __call__(
self, qc: Circuit, qbits: list[LineQubit], memory: dict[str, int]
) -> None:
g = self.gate(qbits, memory)
qc.append(g) # type: ignore


@dataclass
class Pgm:
Expand All @@ -551,10 +642,7 @@ def __init__(self, n: str, body: Cmd | None = None) -> None:
self.body = body or HoleCmd()

def __str__(self) -> str:
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)}"
return f"def pgm({self.n}):\n{indent(str(self.body), TAB)}"

def __repr__(self) -> str:
return f"Pgm({repr(self.body)})"
Expand All @@ -570,6 +658,13 @@ def cost(self) -> int:
def depth(self) -> int:
return self.body.depth

def __call__(self, n: int) -> Circuit:
circuit = Circuit()
qbits = LineQubit.range(n) # type: ignore
memory: dict[str, int] = {self.n: n}
self.body(circuit, qbits, memory)
return circuit


ALL_AEXPS: list[type[Aexp]] = [
a
Expand Down
Loading

0 comments on commit c2f038f

Please sign in to comment.