From 067e972308d191fc8ee6f2119b1af47b39456398 Mon Sep 17 00:00:00 2001 From: HaYeong Lee <110161434+glorialeezero@users.noreply.github.com> Date: Tue, 26 Nov 2024 17:05:07 +0900 Subject: [PATCH 1/5] Update language.py --- qupsy/language.py | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/qupsy/language.py b/qupsy/language.py index 8110f63..146643e 100644 --- a/qupsy/language.py +++ b/qupsy/language.py @@ -235,7 +235,10 @@ 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) + b = self.b(memory) + if b == 0: + raise ValueError("Division by zero") + return self.a(memory) // b @dataclass @@ -327,6 +330,8 @@ def children(self) -> list[Aexp]: def __call__(self, qbits: list[LineQubit], memory: dict[str, int]) -> Gate: idx = self.qreg(memory) + if idx >= len(qbits) or idx < 0: + raise ValueError(f"Index out of range: {idx} >= {len(qbits)}") return cirq.H(qbits[idx]) # type: ignore @@ -353,6 +358,8 @@ def children(self) -> list[Aexp]: def __call__(self, qbits: list[LineQubit], memory: dict[str, int]) -> Gate: idx = self.qreg(memory) + if idx >= len(qbits) or idx < 0: + raise ValueError(f"Index out of range: {idx} >= {len(qbits)}") return cirq.X(qbits[idx]) # type: ignore @@ -385,6 +392,8 @@ def children(self) -> list[Aexp]: def __call__(self, qbits: list[LineQubit], memory: dict[str, int]) -> Gate: idx = self.qreg(memory) + if idx >= len(qbits) or idx < 0: + raise ValueError(f"Index out of range: {idx} >= {len(qbits)}") return cirq.Ry(rads=2 * np.arccos(np.sqrt(self.p(memory) / self.q(memory))))(qbits[idx]) # type: ignore @@ -414,6 +423,12 @@ def children(self) -> list[Aexp]: def __call__(self, qbits: list[LineQubit], memory: dict[str, int]) -> Gate: idx1 = self.qreg1(memory) idx2 = self.qreg2(memory) + if idx1 >= len(qbits) or idx1 < 0: + raise ValueError(f"Index out of range: {idx1} >= {len(qbits)}") + if idx2 >= len(qbits) or idx2 < 0: + raise ValueError(f"Index out of range: {idx2} >= {len(qbits)}") + if idx1 == idx2: + raise ValueError("Control and target qubits must be different") return cirq.CX(qbits[idx1], qbits[idx2]) # type: ignore @@ -453,6 +468,12 @@ def children(self) -> list[Aexp]: def __call__(self, qbits: list[LineQubit], memory: dict[str, int]) -> Gate: idx1 = self.qreg1(memory) idx2 = self.qreg2(memory) + if idx1 >= len(qbits) or idx1 < 0: + raise ValueError(f"Index out of range: {idx1} >= {len(qbits)}") + if idx2 >= len(qbits) or idx2 < 0: + raise ValueError(f"Index out of range: {idx2} >= {len(qbits)}") + if idx1 == idx2: + raise ValueError("Control and target qubits must be different") 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 @@ -601,7 +622,8 @@ def __call__( for i in range(start, end): memory[self.var] = i self.body(qc, qbits, memory) - del memory[self.var] + if self.var in memory: + del memory[self.var] @dataclass @@ -658,6 +680,10 @@ def cost(self) -> int: def depth(self) -> int: return self.body.depth + @property + def terminated(self) -> bool: + return self.body.terminated + def __call__(self, n: int) -> Circuit: circuit = Circuit() qbits = LineQubit.range(n) # type: ignore From 0c123ecbd7df15de94e137d6e01a99fd5fbbc418 Mon Sep 17 00:00:00 2001 From: HaYeong Lee <110161434+glorialeezero@users.noreply.github.com> Date: Tue, 26 Nov 2024 17:05:24 +0900 Subject: [PATCH 2/5] Create search.py --- qupsy/search.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 qupsy/search.py diff --git a/qupsy/search.py b/qupsy/search.py new file mode 100644 index 0000000..592e940 --- /dev/null +++ b/qupsy/search.py @@ -0,0 +1,26 @@ +from qupsy.language import Pgm +from qupsy.spec import Spec +from qupsy.transition import next +from qupsy.utils import logger +from qupsy.verify import verify +from qupsy.worklist import Worklist + + +def search(spec: Spec, *, initial_pgm: Pgm | None = None, n: str = "n") -> Pgm: + worklist = Worklist() + worklist.put(initial_pgm or Pgm(n)) + while worklist.notEmpty(): + current_pgm = worklist.get() + logger.debug(f"Current program: {current_pgm}") + verified: list[bool] = [] + for testcase in spec.testcases: + if current_pgm.terminated: + if verify(testcase, current_pgm): + verified.append(True) + if len(verified) == len(spec.testcases): + return current_pgm + else: + break + else: + worklist.put(*next(current_pgm, spec.gates)) + raise ValueError("No solution found") From 61ae33ea01f185d82d4e144b71580e83f409c4fd Mon Sep 17 00:00:00 2001 From: HaYeong Lee <110161434+glorialeezero@users.noreply.github.com> Date: Tue, 26 Nov 2024 17:05:33 +0900 Subject: [PATCH 3/5] Update transition.py --- qupsy/transition.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/qupsy/transition.py b/qupsy/transition.py index 35e3759..1d0e52f 100644 --- a/qupsy/transition.py +++ b/qupsy/transition.py @@ -17,9 +17,11 @@ class TransitionVisitor: - def __init__(self) -> None: + + def __init__(self, components: list[type[Gate]]): self.n = "" self.for_depth = 0 + self.components = components def visit_HoleAexp(self, aexp: HoleAexp) -> list[Aexp]: return ( @@ -54,7 +56,7 @@ def visit_Aexp(self, aexp: Aexp) -> list[Aexp]: return [] def visit_HoleGate(self, gate: HoleGate) -> list[Gate]: - return [g() for g in ALL_GATES if g != HoleGate] + return [g() for g in self.components if g != HoleGate] def visit_Gate(self, gate: Gate) -> list[Gate]: visitor_name = f"visit_{gate.__class__.__name__}" @@ -163,6 +165,7 @@ def visit(self, pgm: Pgm) -> list[Pgm]: return [Pgm(pgm.n, body) for body in bodies] -def next(pgm: Pgm) -> list[Pgm]: - visitor = TransitionVisitor() +def next(pgm: Pgm, components: list[type[Gate]] | None = None) -> list[Pgm]: + components = components or ALL_GATES + visitor = TransitionVisitor(components) return visitor.visit(pgm) From 2cc1966a2e5d9835a3e312f92b52de06efedb0a0 Mon Sep 17 00:00:00 2001 From: HaYeong Lee <110161434+glorialeezero@users.noreply.github.com> Date: Tue, 26 Nov 2024 17:05:38 +0900 Subject: [PATCH 4/5] Update verify.py --- qupsy/verify.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/qupsy/verify.py b/qupsy/verify.py index 472e074..c4eef7d 100644 --- a/qupsy/verify.py +++ b/qupsy/verify.py @@ -10,6 +10,9 @@ def verify(testcase: tuple[npt.ArrayLike, npt.ArrayLike], pgm: Pgm) -> bool: assert isinstance(input, np.ndarray) assert isinstance(output, np.ndarray) n = int(np.log2(input.size)) - pgm_qc = pgm(n) - pgm_sv = cirq.final_state_vector(pgm_qc, initial_state=input, qubit_order=cirq.LineQubit.range(n)) # type: ignore - return cirq.linalg.allclose_up_to_global_phase(output, pgm_sv) # type: ignore + try: + pgm_qc = pgm(n) + pgm_sv = cirq.final_state_vector(pgm_qc, initial_state=input, qubit_order=cirq.LineQubit.range(n)) # type: ignore + return cirq.linalg.allclose_up_to_global_phase(output, pgm_sv) # type: ignore + except ValueError: + return False From fad84c61398869559a6c38a38ea638e70040206b Mon Sep 17 00:00:00 2001 From: HaYeong Lee <110161434+glorialeezero@users.noreply.github.com> Date: Tue, 26 Nov 2024 17:05:43 +0900 Subject: [PATCH 5/5] Update bin.py --- qupsy/bin.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/qupsy/bin.py b/qupsy/bin.py index a40bd95..87e511f 100644 --- a/qupsy/bin.py +++ b/qupsy/bin.py @@ -2,6 +2,7 @@ from pathlib import Path from typing import cast +from qupsy.search import search from qupsy.spec import parse_spec from qupsy.utils import logger @@ -29,4 +30,9 @@ def main() -> None: if args.dry_run: return - print("Hello, qupsy!") + logger.info("Searching for a solution") + try: + pgm = search(spec) + logger.info("Solution found: %s", pgm) + except ValueError: + logger.error("No solution found")