From f40f340442abb0de19b714f0251eb0b654f49b96 Mon Sep 17 00:00:00 2001 From: vishnu-m77 Date: Thu, 18 Jun 2026 13:01:40 -0400 Subject: [PATCH 1/2] clean: Move math into atomic compiler --- src/oqd_core/compiler/atomic/canonicalize.py | 4 +- src/oqd_core/compiler/atomic/math/__init__.py | 17 + src/oqd_core/compiler/atomic/math/passes.py | 72 +++ src/oqd_core/compiler/atomic/math/rules.py | 527 ++++++++++++++++++ 4 files changed, 618 insertions(+), 2 deletions(-) create mode 100644 src/oqd_core/compiler/atomic/math/__init__.py create mode 100644 src/oqd_core/compiler/atomic/math/passes.py create mode 100644 src/oqd_core/compiler/atomic/math/rules.py diff --git a/src/oqd_core/compiler/atomic/canonicalize.py b/src/oqd_core/compiler/atomic/canonicalize.py index 5f41d9c3..e203986b 100644 --- a/src/oqd_core/compiler/atomic/canonicalize.py +++ b/src/oqd_core/compiler/atomic/canonicalize.py @@ -16,9 +16,9 @@ from oqd_compiler_infrastructure import Chain, Post, Pre, RewriteRule -from oqd_core.compiler.math.rules import SubstituteMathVar +from oqd_core.compiler.atomic.math.rules import SubstituteMathVar from oqd_core.interface.atomic import Level, Transition -from oqd_core.interface.math import MathVar +from oqd_core.interface.atomic import MathVar ######################################################################################## diff --git a/src/oqd_core/compiler/atomic/math/__init__.py b/src/oqd_core/compiler/atomic/math/__init__.py new file mode 100644 index 00000000..92906e43 --- /dev/null +++ b/src/oqd_core/compiler/atomic/math/__init__.py @@ -0,0 +1,17 @@ +# Copyright 2024-2025 Open Quantum Design + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from . import passes, rules + +__all__ = ["passes", "rules"] diff --git a/src/oqd_core/compiler/atomic/math/passes.py b/src/oqd_core/compiler/atomic/math/passes.py new file mode 100644 index 00000000..439e6028 --- /dev/null +++ b/src/oqd_core/compiler/atomic/math/passes.py @@ -0,0 +1,72 @@ +# Copyright 2024-2025 Open Quantum Design + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +######################################################################################## + +from oqd_compiler_infrastructure import Chain, FixedPoint, Post + +from oqd_core.compiler.atomic.math.rules import ( + DistributeMathExpr, + EvaluateMathExpr, + PartitionMathExpr, + PrintMathExpr, + ProperOrderMathExpr, + PruneMathExpr, + SimplifyMathExpr, +) + +######################################################################################## + +__all__ = [ + "evaluate_math_expr", + "simplify_math_expr", + "print_math_expr", + "canonicalize_math_expr", +] + +######################################################################################## + +evaluate_math_expr = Post(EvaluateMathExpr()) +""" +Pass for evaluating math expression +""" + +simplify_math_expr = Post(SimplifyMathExpr()) +""" +Pass for simplifying math expression +""" + +print_math_expr = Post(PrintMathExpr()) +""" +Pass for printing math expression +""" + +canonicalize_math_expr = Chain( + FixedPoint( + Post( + Chain( + PruneMathExpr(), + SimplifyMathExpr(), + DistributeMathExpr(), + ProperOrderMathExpr(), + ) + ) + ), + FixedPoint(Post(PartitionMathExpr())), + simplify_math_expr, +) + +""" +Pass for canonicalizing math expression +""" diff --git a/src/oqd_core/compiler/atomic/math/rules.py b/src/oqd_core/compiler/atomic/math/rules.py new file mode 100644 index 00000000..84db333b --- /dev/null +++ b/src/oqd_core/compiler/atomic/math/rules.py @@ -0,0 +1,527 @@ +# Copyright 2024-2025 Open Quantum Design + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Union + +import numpy as np +from oqd_compiler_infrastructure import ConversionRule, Post, RewriteRule +from pydantic import TypeAdapter, ValidationError + +from oqd_core.interface.atomic import ( + ConstantMathExpr, + MathAdd, + MathBinaryOp, + MathDiv, + MathExpr, + MathFunc, + MathImag, + MathMul, + MathNum, + MathPow, + MathSub, + MathTerminal, + MathVar, + Access, +) + +######################################################################################## + +__all__ = [ + "PrintMathExpr", + "DistributeMathExpr", + "PartitionMathExpr", + "ProperOrderMathExpr", + "PruneMathExpr", + "SimplifyMathExpr", + "EvaluateMathExpr", + "PruneMathExpr", +] + +######################################################################################## + + +class PrintMathExpr(ConversionRule): + """ + This prints [`MathExpr`][oqd_core.interface.math.MathExpr] objects. Verbosity level can be given as an attribute. + + Args: + model (MathExpr): The rule only acts on [`MathExpr`][oqd_core.interface.math.MathExpr] objects. + + Returns: + string (str): + + Assumptions: + None + + Example: + MathAdd(expr1 = 2, expr2 = 3) => str(2 + 3) + """ + + def __init__(self, *, verbose=False): + super().__init__() + + self.verbose = verbose + + def map_MathVar(self, model: MathVar, operands): + string = "{}".format(model.name) + return string + + def map_MathNum(self, model: MathNum, operands): + string = "{}".format(model.value) + return string + + def map_MathImag(self, model: MathImag, operands): + string = "1j" + return string + + def map_MathFunc(self, model: MathFunc, operands): + string = "{}({})".format(model.func, operands["expr"]) + return string + + def map_MathAdd(self, model: MathAdd, operands): + if self.verbose: + return self._map_MathBinaryOp(model, operands) + string = "{} + {}".format(operands["expr1"], operands["expr2"]) + return string + + def map_MathSub(self, model: MathSub, operands): + if self.verbose: + return self._map_MathBinaryOp(model, operands) + s2 = ( + f"({operands['expr2']})" + if isinstance(model.expr2, (MathAdd, MathSub)) + else operands["expr2"] + ) + string = "{} - {}".format(operands["expr1"], s2) + return string + + def map_MathMul(self, model: MathMul, operands): + if self.verbose: + return self._map_MathBinaryOp(model, operands) + s1 = ( + f"({operands['expr1']})" + if isinstance(operands["expr1"], (MathAdd, MathSub)) + else operands["expr1"] + ) + s2 = ( + f"({operands['expr2']})" + if isinstance(model.expr2, (MathAdd, MathSub)) + else operands["expr2"] + ) + + string = "{} * {}".format(s1, s2) + return string + + def map_MathDiv(self, model: MathDiv, operands): + if self.verbose: + return self._map_MathBinaryOp(model, operands) + s1 = ( + f"({operands['expr1']})" + if isinstance(model.expr1, (MathAdd, MathSub)) + else operands["expr1"] + ) + s2 = ( + f"({operands['expr2']})" + if isinstance(model.expr2, (MathAdd, MathSub)) + else operands["expr2"] + ) + + string = "{} / {}".format(s1, s2) + return string + + def map_MathPow(self, model: MathPow, operands): + if self.verbose: + return self._map_MathBinaryOp(model, operands) + s1 = ( + f"({operands['expr1']})" + if isinstance(model.expr1, (MathAdd, MathSub, MathMul, MathDiv)) + else operands["expr1"] + ) + s2 = ( + f"({operands['expr2']})" + if isinstance(model.expr2, (MathAdd, MathSub, MathMul, MathDiv)) + else operands["expr2"] + ) + + string = "{} ** {}".format(s1, s2) + return string + + def _map_MathBinaryOp(self, model: MathBinaryOp, operands): + s1 = ( + f"({operands['expr1']})" + if not isinstance(model.expr1, (MathTerminal, MathFunc)) + else operands["expr1"] + ) + s2 = ( + f"({operands['expr2']})" + if not isinstance(model.expr2, (MathTerminal, MathFunc)) + else operands["expr2"] + ) + operator_dict = dict( + MathAdd="+", MathSub="-", MathMul="*", MathDiv="/", MathPow="**" + ) + string = f"{s1} {operator_dict[model.__class__.__name__]} {s2}" + return string + + +######################################################################################## + + +class DistributeMathExpr(RewriteRule): + """ + This distributes [`MathExpr`][oqd_core.interface.math.MathExpr] objects. + + Args: + model (MathExpr): The rule only acts on [`MathExpr`][oqd_core.interface.math.MathExpr] objects. + + Returns: + model (MathExpr): + + Assumptions: + None + + Example: + MathStr(string = '3 * (2 + 1)') => MathStr(string = '3 * 2 + 3 * 1') + """ + + def map_MathMul(self, model: MathMul): + if isinstance(model.expr1, (MathAdd, MathSub)): + return model.expr1.__class__( + expr1=MathMul(expr1=model.expr1.expr1, expr2=model.expr2), + expr2=MathMul(expr1=model.expr1.expr2, expr2=model.expr2), + ) + if isinstance(model.expr2, (MathAdd, MathSub)): + return model.expr2.__class__( + expr1=MathMul(expr1=model.expr1, expr2=model.expr2.expr1), + expr2=MathMul(expr1=model.expr1, expr2=model.expr2.expr2), + ) + pass + + def map_MathSub(self, model: MathSub): + return MathAdd( + expr1=model.expr1, + expr2=MathMul(expr1=MathNum(value=-1), expr2=model.expr2), + ) + + def map_MathDiv(self, model: MathDiv): + return MathMul( + expr1=model.expr1, + expr2=MathPow(expr1=model.expr2, expr2=MathNum(value=-1)), + ) + + +class PartitionMathExpr(RewriteRule): + """ + This separates real and complex portions of [`MathExpr`][oqd_core.interface.math.MathExpr] objects. + + Args: + model (MathExpr): The rule only acts on [`MathExpr`][oqd_core.interface.math.MathExpr] objects. + + Returns: + model (MathExpr): + + Assumptions: + [`DistributeMathExpr`][oqd_core.compiler.math.rules.DistributeMathExpr], + [`ProperOrderMathExpr`][oqd_core.compiler.math.rules.ProperOrderMathExpr] + + Example: + - MathStr(string = '1 + 1j + 2') => MathStr(string = '1 + 2 + 1j') + - MathStr(string = '1 * 1j * 2') => MathStr(string = '1j * 1 * 2') + """ + + def map_MathAdd(self, model): + priority = dict( + MathImag=6, MathNum=5, MathVar=4, Access=3, MathFunc=2, MathPow=1, MathMul=0 + ) + + if isinstance( + model.expr2, (MathImag, MathNum, MathVar, Access, MathFunc, MathPow, MathMul) + ): + if isinstance(model.expr1, MathAdd): + if ( + priority[model.expr2.__class__.__name__] + > priority[model.expr1.expr2.__class__.__name__] + ): + return MathAdd( + expr1=MathAdd(expr1=model.expr1.expr1, expr2=model.expr2), + expr2=model.expr1.expr2, + ) + else: + if ( + priority[model.expr2.__class__.__name__] + > priority[model.expr1.__class__.__name__] + ): + return MathAdd( + expr1=model.expr2, + expr2=model.expr1, + ) + + def map_MathMul(self, model: MathMul): + priority = dict(MathImag=5, MathNum=4, MathVar=3, Access=2, MathFunc=1, MathPow=0) + + if isinstance(model.expr2, (MathImag, MathNum, MathVar, Access, MathFunc, MathPow)): + if isinstance(model.expr1, MathMul): + if ( + priority[model.expr2.__class__.__name__] + > priority[model.expr1.expr2.__class__.__name__] + ): + return MathMul( + expr1=MathMul(expr1=model.expr1.expr1, expr2=model.expr2), + expr2=model.expr1.expr2, + ) + else: + if ( + priority[model.expr2.__class__.__name__] + > priority[model.expr1.__class__.__name__] + ): + return MathMul( + expr1=model.expr2, + expr2=model.expr1, + ) + + +class ProperOrderMathExpr(RewriteRule): + """ + This rearranges bracketing of [`MathExpr`][oqd_core.interface.math.MathExpr] objects. + + Args: + model (MathExpr): The rule only acts on [`MathExpr`][oqd_core.interface.math.MathExpr] objects. + + Returns: + model (MathExpr): + + Assumptions: + [`DistributeMathExpr`][oqd_core.compiler.math.rules.DistributeMathExpr] + + Example: + - MathStr(string = '2 * (3 * 5)') => MathStr(string = '(2 * 3) * 5') + """ + + def map_MathAdd(self, model: MathAdd): + return self._MathAddMul(model) + + def map_MathMul(self, model: MathMul): + return self._MathAddMul(model) + + def _MathAddMul(self, model: Union[MathAdd, MathMul]): + if isinstance(model.expr2, model.__class__): + return model.__class__( + expr1=model.__class__(expr1=model.expr1, expr2=model.expr2.expr1), + expr2=model.expr2.expr2, + ) + pass + + +class PruneMathExpr(RewriteRule): + """ + This is constant fold operation where scalar addition, multiplication and power are simplified + + Args: + model (MathExpr): The rule only acts on [`MathExpr`][oqd_core.interface.math.MathExpr] objects. + + Returns: + model (MathExpr): + + Assumptions: + None + + """ + + def map_MathAdd(self, model): + if model.expr1 == MathNum(value=0): + return model.expr2 + if model.expr2 == MathNum(value=0): + return model.expr1 + + def map_MathMul(self, model): + if model.expr1 == MathNum(value=1): + return model.expr2 + if model.expr2 == MathNum(value=1): + return model.expr1 + + if model.expr1 == MathNum(value=0) or model.expr2 == MathNum(value=0): + return MathNum(value=0) + + def map_MathPow(self, model): + if model.expr1 == MathNum(value=1) or model.expr2 == MathNum(value=1): + return model.expr1 + + if model.expr2 == MathNum(value=0): + return MathNum(value=1) + + if model.expr1 == MathNum(value=0): + return MathNum(value=0) + + +######################################################################################## + + +class SubstituteMathVar(RewriteRule): + """ + This rule substitutes a MathVar with another MathExpr + + Args: + model (MathExpr): The rule only acts on [`MathExpr`][oqd_core.interface.math.MathExpr] objects. + + Returns: + model (MathExpr): + + Assumptions: + None + + """ + + def __init__(self, variable, substitution): + super().__init__() + + if not isinstance(variable, MathVar): + raise TypeError("Variable must be a MathVar") + + if not isinstance(variable, MathExpr): + raise TypeError("Substituted value must be a MathExpr") + + self.variable = variable + self.substitution = substitution + + def map_MathVar(self, model): + if model == self.variable: + return self.substitution + + +######################################################################################## + + +class EvaluateMathExpr(ConversionRule): + """ + This evaluates MathExpr objects and raises a type error if a MathVar exist in the AST. + + Args: + model (MathExpr): The rule only acts on [`MathExpr`][oqd_core.interface.math.MathExpr] objects. + + Returns: + model (MathExpr): + + Assumptions: + None + """ + + def map_MathVar(self, model: MathVar, operands): + raise TypeError( + "Evaluation requires the substitution of all MathVar to constants" + ) + + def map_Access(self, model: Access, operands): + raise TypeError( + "Evaluation requires Access to be resolved" + ) + + def map_MathNum(self, model: MathNum, operands): + return model.value + + def map_MathImag(self, model: MathImag, operands): + return complex("1j") + + def map_MathFunc(self, model: MathFunc, operands): + if model.func in [ + "abs", + "sin", + "cos", + "tan", + "exp", + "log", + "sinh", + "cosh", + "tanh", + "atan", + "acos", + "asin", + "atanh", + "asinh", + "acosh", + "conj", + "real", + "imag", + "atan2", + ]: + if isinstance(operands["expr"], list): + return getattr(np, model.func)(*operands["expr"]) + + return getattr(np, model.func)(operands["expr"]) + + if model.func == "heaviside": + return np.heaviside(operands["expr"], 1) + + raise ValueError("Unsupported function") + + def map_MathAdd(self, model: MathAdd, operands): + return operands["expr1"] + operands["expr2"] + + def map_MathSub(self, model: MathSub, operands): + return operands["expr1"] - operands["expr2"] + + def map_MathMul(self, model: MathMul, operands): + return operands["expr1"] * operands["expr2"] + + def map_MathDiv(self, model: MathDiv, operands): + return operands["expr1"] / operands["expr2"] + + def map_MathPow(self, model: MathPow, operands): + return operands["expr1"] ** operands["expr2"] + + +######################################################################################## + + +class SimplifyMathExpr(RewriteRule): + """ + This simplifies MathExpr objects by evaluating all constants in the AST. + + Args: + model (MathExpr): The rule only acts on [`MathExpr`][oqd_core.interface.math.MathExpr] objects. + + Returns: + model (MathExpr): + + Assumptions: + None + + """ + + def map_MathNum(self, model): + # This empty function overrides the map_MathExpr definition for child class MathNum + pass + + def map_MathImag(self, model): + # This empty function overrides the map_MathExpr definition for child class MathImag + pass + + def map_MathExpr(self, model): + try: + TypeAdapter(ConstantMathExpr).validate_python(model) + + value = Post(EvaluateMathExpr())(model) + + if isinstance(value, (int, float)): + return MathNum(value=value) + elif value == 1j: + return MathImag() + elif value.real == 0: + return MathImag() * MathNum(value=value.imag) + else: + return MathNum(value=value.real) + MathImag() * MathNum( + value=value.imag + ) + + except (ValidationError, TypeError, ValueError): + return model From 8232c4cb29e2a49e9ef572d3a81c4b36075e4884 Mon Sep 17 00:00:00 2001 From: vishnu-m77 Date: Fri, 19 Jun 2026 16:22:38 -0400 Subject: [PATCH 2/2] feat: Fix atomic canonicalization passes --- .github/workflows/pytest.yml | 2 +- examples/analog/test.analog | 4 +- examples/analog/test.ipynb | 199 ++++++----- examples/atomic/test.atomic | 1 + examples/atomic/test.ipynb | 47 ++- src/oqd_core/analysis/analog/types.py | 6 +- src/oqd_core/compiler/atomic/canonicalize.py | 326 +++++++----------- .../compiler/atomic/cfg_passes/__init__.py | 6 + .../compiler/atomic/cfg_passes/walk.py | 89 +++++ src/oqd_core/compiler/atomic/error.py | 27 ++ src/oqd_core/compiler/atomic/math/rules.py | 4 +- tests/test_atomic/test_beam.py | 162 ++++----- tests/test_atomic/test_unroll.py | 265 -------------- tests/test_serialization.py | 257 -------------- 14 files changed, 491 insertions(+), 904 deletions(-) create mode 100644 src/oqd_core/compiler/atomic/cfg_passes/__init__.py create mode 100644 src/oqd_core/compiler/atomic/cfg_passes/walk.py create mode 100644 src/oqd_core/compiler/atomic/error.py delete mode 100644 tests/test_atomic/test_unroll.py delete mode 100644 tests/test_serialization.py diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 65838077..12ff5ddb 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -39,4 +39,4 @@ jobs: uv sync --extra tests - name: Run tests run: | - uv run pytest tests/test_frontend tests/test_analog tests/test_analysis -v + uv run pytest tests/ -v diff --git a/examples/analog/test.analog b/examples/analog/test.analog index d452fbac..0650e435 100644 --- a/examples/analog/test.analog +++ b/examples/analog/test.analog @@ -35,7 +35,7 @@ if (x > 0){ initialize(r) } if (x == 1) { - evolve(H_single, 1.0, r) + evolve(H_single, 1.0, q0) } else { evolve(H_pair, 0.5, r) } n = 3 @@ -67,7 +67,7 @@ if (a > b) { initialize(r) // evolve(hamiltonian, duration, target) -evolve(H_single, 2.0, r) +evolve(H_single, 2.0, q1) evolve(H_pair, 1.0, targets) evolve(%X %* 0.5, pi, q0) diff --git a/examples/analog/test.ipynb b/examples/analog/test.ipynb index e4db5684..0f231658 100644 --- a/examples/analog/test.ipynb +++ b/examples/analog/test.ipynb @@ -34,111 +34,152 @@ "execution_count": 2, "id": "1c5481c1", "metadata": {}, + "outputs": [], + "source": [ + "from oqd_compiler_infrastructure import Post, PrettyPrint\n", + "\n", + "from oqd_core.analysis.analog.cfg import AnalogCFGBuilder\n", + "from oqd_core.analysis.analog.type_checker import AnalogTypeChecker\n", + "from oqd_core.frontend.analog import parse_analog\n", + "\n", + "printer = Post(PrettyPrint())\n", + "\n", + "with open(\"test.analog\", mode=\"r\", encoding=\"utf8\") as f:\n", + " source = f.read()\n", + "\n", + "circuit = parse_analog(source)\n", + "cfg = AnalogCFGBuilder().run(circuit)\n", + "checker = AnalogTypeChecker(cfg)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "08b33d2c", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "line 1:8 no viable alternative at input 'r = qreg['\n" + ] + } + ], + "source": [ + "from oqd_core.frontend.analog import parse_analog\n", + "\n", + "program = \"r = qreg[2] \\n H_single = 0.5 %* %X %+ 0.5 %* %Z \\n evolve(H_single, 1.0, r[0])\"\n", + "circuit = parse_analog(program)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "f628d79e", + "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "r = qreg(2)\n", - "s = qmode(3)\n", - "q0 = r[0]\n", - "q1 = r[1]\n", - "targets = [q0, q1]\n", - "pi = 3.14159\n", - "tau = 2 * pi\n", - "omega = #omega\n", - "phase = #t * omega + #phi\n", - "neg = -1\n", - "cubed = 2 ^ 3\n", - "sine = sin(0.5)\n", - "cosed = cos(phase)\n", - "cplx = 1.0 + 1j * 0.5\n", - "X = %X\n", - "Y = %Y\n", - "Z = %Z\n", - "I = %I\n", - "C = %C\n", - "A = %A\n", - "J = %J\n", - "H_single = 0.5 * %X + 0.5 * %Z\n", - "H_pair = %X %@ %I %+ %I %@ %X\n", - "H_rabi = 0.5 * #t * %X %@ %I %+ 0.5 * %I %@ %Y\n", - "x = 1\n", - "if (x > 0) {\n", - "initialize(r)\n", - "}\n", - "if (x == 1) {\n", - "evolve(H_single, 1.0, r)\n", - "} else {\n", - "evolve(H_pair, 0.5, r)\n", - "}\n", - "n = 3\n", - "while (n > 0) {\n", - "evolve(H_rabi, 0.1, targets)\n", - "n = n - 1\n", - "}\n", - "while (true) {\n", - "if (x == 1) {\n", - "break\n", - "}\n", - "}\n", - "a = 5\n", - "b = 3\n", - "if (a > b) {\n", - "if (b > 0) {\n", - "initialize(targets)\n", - "} else {\n", - "measure(r)\n", - "}\n", - "}\n", - "initialize(r)\n", - "evolve(H_single, 2.0, r)\n", - "evolve(H_pair, 1.0, targets)\n", - "evolve(%X * 0.5, pi, q0)\n", - "measure(r)\n", - "measurement = measure(q0)\n", - "flag = 1\n", - "if (flag != 0) {\n", - "x = 0\n", - "}\n", - "if (x <= 1) {\n", - "x = 1\n", - "}\n", - "if (x >= 0) {\n", - "x = 0\n", - "}\n", - "a = true\n", - "if (a) {\n", - "b = 5\n", - "}\n", - "\n" + "\"in_env={0: {}, 1: {}, 2: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None)}, 3: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None)}, 4: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None)}, 5: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None)}, 6: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 7: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 8: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 9: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 10: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 11: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 12: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 13: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 14: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 15: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 16: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 17: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 18: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 19: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 20: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 21: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 22: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 23: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 24: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 25: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 26: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 27: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 28: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 29: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 30: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 31: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 32: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 33: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 34: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 35: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 36: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 37: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 38: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 39: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 40: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 41: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 42: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 43: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 44: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 45: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 46: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 47: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 48: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 49: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 50: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 51: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 52: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 53: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 54: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 55: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 56: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 57: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 58: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 59: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}, 60: {'r': SymbolBinding(lattice_type=, target_dim=(2, 0), list_elem=None), 's': SymbolBinding(lattice_type=, target_dim=(0, 3), list_elem=None), 'q0': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'q1': SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None), 'targets': SymbolBinding(lattice_type=TList(elem=), target_dim=(2, 0), list_elem=SymbolBinding(lattice_type=, target_dim=(1, 0), list_elem=None))}} stmt_index={4492982736: 1, 4492977376: 2, 4492983696: 3, 4492979216: 4, 4492981776: 5, 4474730720: 6, 4493035552: 7, 4493036512: 8, 4493038112: 9, 4493038432: 10, 4493038752: 11, 4493038912: 12, 4493039312: 13, 4493039952: 14, 4493040112: 15, 4493040272: 16, 4493040352: 17, 4493040512: 18, 4493040672: 19, 4493040832: 20, 4493040992: 21, 4493042112: 22, 4493043312: 23, 4493044672: 24, 4493044832: 25, 4493045072: 26, 4493045392: 27, 4493045712: 28, 4493046032: 29, 4493046512: 30, 4493046752: 31, 4493046992: 32, 4493047312: 33, 4493047632: 34, 4485421872: 35, 4493048112: 36, 4493048272: 37, 4493048512: 38, 4493048672: 39, 4493048912: 40, 4493049152: 41, 4493049312: 42, 4493049472: 43, 4497948752: 44, 4497949072: 45, 4497949392: 46, 4497949872: 47, 4497950032: 48, 4497950272: 49, 4497950432: 50, 4497950672: 51, 4497950912: 52, 4497951152: 53, 4497951392: 54, 4497951792: 55, 4497951952: 56, 4497952272: 57, 4497952192: 58, 4497952432: 59}\"\n" ] } ], "source": [ - "from oqd_core.frontend.analog import parse_analog, serialize_analog\n", + "from oqd_core.analysis.analog.symbol_table import AnalogSymbolTableBuilder\n", + "\n", + "symbol_analysis = AnalogSymbolTableBuilder(cfg, checker.dataflow_result)\n", + "symbol_table = symbol_analysis.symbol_table\n", + "import json\n", + "\n", + "symbol_table = str(symbol_table)\n", + "print(json.dumps(symbol_table, indent=2))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "ec743f9f", + "metadata": {}, + "outputs": [ + { + "ename": "AnalogCompilerError", + "evalue": "Inconsistent Hilbert space dimensions between Evolve statements", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mAnalogCompilerError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[5]\u001b[39m\u001b[32m, line 17\u001b[39m\n\u001b[32m 15\u001b[39m symbol_analysis = AnalogSymbolTableBuilder(cfg, dataflow_result)\n\u001b[32m 16\u001b[39m symbol_table = symbol_analysis.symbol_table\n\u001b[32m---> \u001b[39m\u001b[32m17\u001b[39m \u001b[43mcompile_analog_circuit\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcircuit\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcfg\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msymbol_table\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Documents/OQD/oqd-core/src/oqd_core/compiler/analog/passes/compile.py:46\u001b[39m, in \u001b[36mcompile_analog_circuit\u001b[39m\u001b[34m(model, cfg, symbol_table, args)\u001b[39m\n\u001b[32m 43\u001b[39m verify_register_access_dim(cfg, symbol_table)\n\u001b[32m 44\u001b[39m verify_hamiltonian_target_dim(cfg, symbol_table)\n\u001b[32m---> \u001b[39m\u001b[32m46\u001b[39m n_qreg, n_qmode = \u001b[43minfer_analog_circuit_dim_cfg\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcfg\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 47\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m args \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 48\u001b[39m canonicalize_args_metrics(args)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Documents/OQD/oqd-core/src/oqd_core/compiler/analog/passes/assign.py:52\u001b[39m, in \u001b[36minfer_analog_circuit_dim_cfg\u001b[39m\u001b[34m(cfg)\u001b[39m\n\u001b[32m 50\u001b[39m dim = d\n\u001b[32m 51\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m dim != d:\n\u001b[32m---> \u001b[39m\u001b[32m52\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m AnalogCompilerError(\u001b[33m\"\u001b[39m\u001b[33mInconsistent Hilbert space dimensions between Evolve statements\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 53\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m dim \u001b[38;5;129;01mor\u001b[39;00m (\u001b[32m0\u001b[39m, \u001b[32m0\u001b[39m)\n", + "\u001b[31mAnalogCompilerError\u001b[39m: Inconsistent Hilbert space dimensions between Evolve statements" + ] + } + ], + "source": [ + "from oqd_core.analysis.analog import (\n", + " AnalogCFGBuilder,\n", + " AnalogSymbolTableBuilder,\n", + " AnalogTypeChecker,\n", + ")\n", + "from oqd_core.compiler.analog.passes.compile import compile_analog_circuit\n", "\n", "with open(\"test.analog\", mode=\"r\", encoding=\"utf8\") as f:\n", " source = f.read()\n", "\n", "circuit = parse_analog(source)\n", - "\n", - "\n", - "print(serialize_analog(circuit))" + "cfg = AnalogCFGBuilder().run(circuit)\n", + "type_checker = AnalogTypeChecker(cfg)\n", + "dataflow_result = type_checker.dataflow_result\n", + "symbol_analysis = AnalogSymbolTableBuilder(cfg, dataflow_result)\n", + "symbol_table = symbol_analysis.symbol_table\n", + "compile_analog_circuit(circuit, cfg, symbol_table)\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "b5a6b113", + "id": "34b5e170", "metadata": {}, "outputs": [], "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8303ca3d", + "metadata": {}, + "outputs": [], + "source": [ + "# from oqd_core.compiler.analog.passes.compile import compile_analog_circuit\n", + "# out, (n_qreg, n_qmode) = compile_analog_circuit(circuit)\n", + "# print(printer(out))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b5a6b113", + "metadata": {}, + "outputs": [], + "source": [ + "# cfg = AnalogCFGBuilder().run(circuit)\n", + "# out = json.dumps({node_id: node.to_dict() for node_id, node in cfg.items()}, indent=2)\n", + "# print(out)\n", + "\n", + "\n", + "# print(symbol_table)\n", + "# symbol_table = str(symbol_table)\n", + "# tree = symbol_table.model_dump_json(indent=2, serialize_as_any=True)\n", + "# print(json.dumps(cfg, indent=2))" + ] } ], "metadata": { "kernelspec": { - "display_name": "oqd-core (3.11.11)", + "display_name": "oqd-core (3.11.11.final.0)", "language": "python", "name": "python3" }, diff --git a/examples/atomic/test.atomic b/examples/atomic/test.atomic index 2ca08c07..30c08182 100644 --- a/examples/atomic/test.atomic +++ b/examples/atomic/test.atomic @@ -11,6 +11,7 @@ phi = #phi t_gate = 10e-6 flag = true n = 3 +r = ionreg(2) // beam(frequency, rabi, phase, polarization, wavevector) beam_mw = beam(2.0 * pi * 1e6, omega, phi, [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]) diff --git a/examples/atomic/test.ipynb b/examples/atomic/test.ipynb index 17d1368a..d7645db1 100644 --- a/examples/atomic/test.ipynb +++ b/examples/atomic/test.ipynb @@ -82,6 +82,7 @@ "t_gate = 1e-5\n", "flag = true\n", "n = 3\n", + "r = ionreg(2)\n", "beam_mw = beam(2.0 * pi * 1000000.0, omega, phi, [1.0, 0.0, 0.0], [0.0, 0.0, 1.0])\n", "beam_det = beam(2.0 * pi * 2000000.0, 0.25, 0.0, [0.0, 1.0, 0.0], [0.0, 0.0, 1.0])\n", "pulse(beam_mw, t_gate, q0, false)\n", @@ -138,16 +139,56 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "89124808", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "from oqd_compiler_infrastructure import Post, PrettyPrint\n", + "\n", + "from oqd_core.analysis.atomic.cfg import AtomicCFGBuilder\n", + "from oqd_core.analysis.atomic.type_checker import AtomicTypeChecker\n", + "from oqd_core.frontend.atomic import parse_atomic\n", + "\n", + "printer = Post(PrettyPrint())\n", + "\n", + "with open(\"test.atomic\", mode=\"r\", encoding=\"utf8\") as f:\n", + " source = f.read()\n", + "\n", + "circuit = parse_atomic(source)\n", + "cfg = AtomicCFGBuilder().run(circuit)\n", + "checker = AtomicTypeChecker(cfg)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "9314af17", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AtomicCircuit(class_='AtomicCircuit', statements=[Declaration(class_='Declaration', name='ions', value=IonRegister(class_='IonRegister', size=2)), Declaration(class_='Declaration', name='q0', value=Extract(class_='Extract', access=Access(class_='Access', name='ions'), index=0)), Declaration(class_='Declaration', name='q1', value=Extract(class_='Extract', access=Access(class_='Access', name='ions'), index=1)), Declaration(class_='Declaration', name='targets', value=AtomicList(class_='AtomicList', values=[Access(class_='Access', name='q0'), Access(class_='Access', name='q1')])), Declaration(class_='Declaration', name='pi', value=MathNum(class_='MathNum', value=3.14159)), Declaration(class_='Declaration', name='omega', value=MathVar(class_='MathVar', name='#omega')), Declaration(class_='Declaration', name='phi', value=MathVar(class_='MathVar', name='#phi')), Declaration(class_='Declaration', name='t_gate', value=MathNum(class_='MathNum', value=1e-05)), Declaration(class_='Declaration', name='flag', value=Bool(class_='Bool', value=True)), Declaration(class_='Declaration', name='n', value=MathNum(class_='MathNum', value=3)), Declaration(class_='Declaration', name='r', value=IonRegister(class_='IonRegister', size=2)), Declaration(class_='Declaration', name='beam_mw', value=Beam(class_='Beam', frequency=MathMul(class_='MathMul', expr1=MathNum(class_='MathNum', value=2000000.0), expr2=Access(class_='Access', name='pi')), rabi=Access(class_='Access', name='omega'), phase=Access(class_='Access', name='phi'), polarization=AtomicList(class_='AtomicList', values=[MathNum(class_='MathNum', value=1.0), MathNum(class_='MathNum', value=0.0), MathNum(class_='MathNum', value=0.0)]), wavevector=AtomicList(class_='AtomicList', values=[MathNum(class_='MathNum', value=0.0), MathNum(class_='MathNum', value=0.0), MathNum(class_='MathNum', value=1.0)]))), Declaration(class_='Declaration', name='beam_det', value=Beam(class_='Beam', frequency=MathMul(class_='MathMul', expr1=MathNum(class_='MathNum', value=4000000.0), expr2=Access(class_='Access', name='pi')), rabi=MathNum(class_='MathNum', value=0.25), phase=MathNum(class_='MathNum', value=0.0), polarization=AtomicList(class_='AtomicList', values=[MathNum(class_='MathNum', value=0.0), MathNum(class_='MathNum', value=1.0), MathNum(class_='MathNum', value=0.0)]), wavevector=AtomicList(class_='AtomicList', values=[MathNum(class_='MathNum', value=0.0), MathNum(class_='MathNum', value=0.0), MathNum(class_='MathNum', value=1.0)]))), SerialProtocol(class_='SerialProtocol', pulses=[Pulse(class_='Pulse', duration=Access(class_='Access', name='t_gate'), target=Access(class_='Access', name='q0'), beam=Access(class_='Access', name='beam_mw'), measured=Bool(class_='Bool', value=False))]), SerialProtocol(class_='SerialProtocol', pulses=[Pulse(class_='Pulse', duration=MathNum(class_='MathNum', value=3e-06), target=Access(class_='Access', name='q1'), beam=Access(class_='Access', name='beam_det'), measured=Bool(class_='Bool', value=True))]), SerialProtocol(class_='SerialProtocol', pulses=[Pulse(class_='Pulse', duration=MathNum(class_='MathNum', value=2e-06), target=Access(class_='Access', name='targets'), beam=Access(class_='Access', name='beam_mw'), measured=Bool(class_='Bool', value=False))]), SerialProtocol(class_='SerialProtocol', pulses=[Pulse(class_='Pulse', duration=Access(class_='Access', name='t_gate'), target=Extract(class_='Extract', access=Access(class_='Access', name='r'), index=0), beam=Access(class_='Access', name='beam_mw'), measured=Bool(class_='Bool', value=False))]), SerialProtocol(class_='SerialProtocol', pulses=[Pulse(class_='Pulse', duration=MathNum(class_='MathNum', value=3e-06), target=Extract(class_='Extract', access=Access(class_='Access', name='r'), index=1), beam=Access(class_='Access', name='beam_det'), measured=Bool(class_='Bool', value=True))]), SerialProtocol(class_='SerialProtocol', pulses=[ParallelProtocol(class_='ParallelProtocol', pulses=[Pulse(class_='Pulse', duration=MathNum(class_='MathNum', value=5e-06), target=Access(class_='Access', name='q0'), beam=Access(class_='Access', name='beam_mw'), measured=Bool(class_='Bool', value=False)), Pulse(class_='Pulse', duration=MathNum(class_='MathNum', value=5e-06), target=Access(class_='Access', name='q1'), beam=Access(class_='Access', name='beam_det'), measured=Bool(class_='Bool', value=False))])]), SerialProtocol(class_='SerialProtocol', pulses=[ParallelProtocol(class_='ParallelProtocol', pulses=[Pulse(class_='Pulse', duration=MathNum(class_='MathNum', value=5e-06), target=Access(class_='Access', name='q0'), beam=Access(class_='Access', name='beam_mw'), measured=Bool(class_='Bool', value=False))]), ParallelProtocol(class_='ParallelProtocol', pulses=[Pulse(class_='Pulse', duration=MathNum(class_='MathNum', value=5e-06), target=Access(class_='Access', name='q1'), beam=Access(class_='Access', name='beam_det'), measured=Bool(class_='Bool', value=False))])]), SerialProtocol(class_='SerialProtocol', pulses=[ParallelProtocol(class_='ParallelProtocol', pulses=[Pulse(class_='Pulse', duration=MathNum(class_='MathNum', value=5e-06), target=Access(class_='Access', name='q0'), beam=Access(class_='Access', name='beam_mw'), measured=Bool(class_='Bool', value=False)), Pulse(class_='Pulse', duration=MathNum(class_='MathNum', value=5e-06), target=Access(class_='Access', name='q0'), beam=Access(class_='Access', name='beam_mw'), measured=Bool(class_='Bool', value=False)), Pulse(class_='Pulse', duration=MathNum(class_='MathNum', value=5e-06), target=Access(class_='Access', name='q1'), beam=Access(class_='Access', name='beam_det'), measured=Bool(class_='Bool', value=False))]), ParallelProtocol(class_='ParallelProtocol', pulses=[Pulse(class_='Pulse', duration=MathNum(class_='MathNum', value=5e-06), target=Access(class_='Access', name='q1'), beam=Access(class_='Access', name='beam_det'), measured=Bool(class_='Bool', value=False))])]), IfElse(class_='IfElse', condition=Access(class_='Access', name='flag'), then_branch=[SerialProtocol(class_='SerialProtocol', pulses=[Pulse(class_='Pulse', duration=MathNum(class_='MathNum', value=2e-06), target=Access(class_='Access', name='targets'), beam=Access(class_='Access', name='beam_mw'), measured=Bool(class_='Bool', value=False))])], else_branch=[SerialProtocol(class_='SerialProtocol', pulses=[Pulse(class_='Pulse', duration=MathNum(class_='MathNum', value=2e-05), target=Access(class_='Access', name='targets'), beam=Access(class_='Access', name='beam_det'), measured=Bool(class_='Bool', value=True))])]), While(class_='While', condition=BoolGreaterThan(class_='BoolGreaterThan', expr1=Access(class_='Access', name='n'), expr2=MathNum(class_='MathNum', value=0)), body=[SerialProtocol(class_='SerialProtocol', pulses=[ParallelProtocol(class_='ParallelProtocol', pulses=[Pulse(class_='Pulse', duration=MathNum(class_='MathNum', value=1e-06), target=Access(class_='Access', name='targets'), beam=Access(class_='Access', name='beam_mw'), measured=Bool(class_='Bool', value=False))])]), Declaration(class_='Declaration', name='n', value=MathAdd(class_='MathAdd', expr1=MathNum(class_='MathNum', value=-1), expr2=Access(class_='Access', name='n'))), IfElse(class_='IfElse', condition=BoolEq(class_='BoolEq', expr1=Access(class_='Access', name='n'), expr2=MathNum(class_='MathNum', value=1)), then_branch=[Continue(class_='Continue')], else_branch=[]), IfElse(class_='IfElse', condition=BoolEq(class_='BoolEq', expr1=Access(class_='Access', name='n'), expr2=MathNum(class_='MathNum', value=0)), then_branch=[Break(class_='Break')], else_branch=[])])])" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from oqd_core.compiler.atomic.canonicalize import canonicalize_atomic_circuit_factory\n", + "from oqd_core.compiler.atomic.cfg_passes.walk import canonicalize_math_cfg\n", + "\n", + "canonicalize_math_cfg(cfg)\n", + "canonicalize_atomic_circuit_factory()(circuit)" + ] } ], "metadata": { "kernelspec": { - "display_name": "oqd-core (3.11.11)", + "display_name": "oqd-core (3.11.11.final.0)", "language": "python", "name": "python3" }, diff --git a/src/oqd_core/analysis/analog/types.py b/src/oqd_core/analysis/analog/types.py index bfbdc008..63253e74 100644 --- a/src/oqd_core/analysis/analog/types.py +++ b/src/oqd_core/analysis/analog/types.py @@ -15,7 +15,6 @@ from __future__ import annotations -from dataclasses import dataclass from typing import Union from oqd_compiler_infrastructure.lattice import ( @@ -23,6 +22,7 @@ LatticeBottom, LatticeTop, ) +from pydantic import BaseModel, ConfigDict from oqd_core.interface.analog import ( BoolAnd, @@ -48,9 +48,9 @@ class AnalogTypeError(TypeError): """Type Error class for Analog.""" pass -@dataclass -class TList(LatticeTop): +class TList(LatticeTop, BaseModel): """Lattice value representing a list.""" + model_config = ConfigDict(frozen=True) elem: TLatticeValue TLatticeValue = Union[TList, type[LatticeTop]] diff --git a/src/oqd_core/compiler/atomic/canonicalize.py b/src/oqd_core/compiler/atomic/canonicalize.py index e203986b..fd0b650e 100644 --- a/src/oqd_core/compiler/atomic/canonicalize.py +++ b/src/oqd_core/compiler/atomic/canonicalize.py @@ -14,98 +14,23 @@ from functools import partial, reduce -from oqd_compiler_infrastructure import Chain, Post, Pre, RewriteRule +from oqd_compiler_infrastructure import Chain, Post, RewriteRule +from oqd_core.interface.atomic.expr import MathNum, MathSub, Pulse +from oqd_core.interface.atomic import Declaration, IfElse, While +from oqd_core.compiler.atomic.math.passes import simplify_math_expr +from oqd_core.compiler.atomic.error import AtomicCompilerError +from oqd_core.interface.atomic.statement import SerialProtocol, ParallelProtocol -from oqd_core.compiler.atomic.math.rules import SubstituteMathVar -from oqd_core.interface.atomic import Level, Transition -from oqd_core.interface.atomic import MathVar ######################################################################################## +PROTOCOL_STMT_TYPES = (Pulse, ParallelProtocol, SerialProtocol) -class UnrollLevelLabel(RewriteRule): - """ - Unrolls the [`Level`][oqd_core.interface.atomic.system.Level] labels present in [`Transitions`][oqd_core.interface.atomic.system.Transition]. - - Args: - model (AtomicCircuit): The rule only acts on [`AtomicCircuit`][oqd_core.interface.atomic.AtomicCircuit] objects. - - Returns: - model (AtomicCircuit): - - Assumptions: - None - - """ - - def map_Ion(self, model): - self.ion_levels = {level.label: level for level in model.levels} - - def map_Transition(self, model): - if isinstance(model.level1, Level) and isinstance(model.level2, Level): - return - - level1 = ( - self.ion_levels[model.level1] - if isinstance(model.level1, str) - else model.level1 - ) - level2 = ( - self.ion_levels[model.level2] - if isinstance(model.level2, str) - else model.level2 - ) - return model.__class__( - label=model.label, - level1=level1, - level2=level2, - einsteinA=model.einsteinA, - multipole=model.multipole, - ) - - -class UnrollTransitionLabel(RewriteRule): - """ - Unrolls the [`Transition`][oqd_core.interface.atomic.system.Transition] labels present in [`Beams`][oqd_core.interface.atomic.protocol.Beam]. - - Args: - model (AtomicCircuit): The rule only acts on [`AtomicCircuit`][oqd_core.interface.atomic.AtomicCircuit] objects. - - Returns: - model (AtomicCircuit): - - Assumptions: - None - """ - - def map_System(self, model): - self.ions_transitions = [ - {transition.label: transition for transition in ion.transitions} - for ion in model.ions - ] - - def map_Beam(self, model): - if isinstance(model.transition, Transition): - return - - if isinstance(model.transition, str): - transition_label = model.transition - reference_ion = model.target - else: - transition_label = model.transition[0] - reference_ion = model.transition[1] - - transition = self.ions_transitions[reference_ion][transition_label] - return model.__class__( - transition=transition, - rabi=model.rabi, - detuning=model.detuning, - phase=model.phase, - polarization=model.polarization, - wavevector=model.wavevector, - target=model.target, - ) - +def _as_numeric_duration(duration): + simplified = simplify_math_expr(duration) + if isinstance(simplified, MathNum): + return simplified.value + raise AtomicCompilerError(f"Duration must be constant: {duration}") class ResolveNestedProtocol(RewriteRule): """ @@ -129,15 +54,19 @@ def __init__(self): @classmethod def _get_continuous_duration(self, model): if isinstance(model, ParallelProtocol): - if len(model.sequence) == 1: - return model.sequence[0].duration + if not model.pulses: + raise AtomicCompilerError(f"Parallel block is empty.") + if len(model.pulses) == 1: + return self._get_continuous_duration(model.pulses[0]) - return min(map(lambda x: x.duration, model.sequence)) + return min(map(lambda x: self._get_continuous_duration, model.pulses)) - if isinstance(model, SequentialProtocol): - return self._get_continuous_duration(model.sequence[0]) + if isinstance(model, SerialProtocol): + if not model.pulses: + raise AtomicCompilerError(f"Serial block is empty.") + return self._get_continuous_duration(model.pulses[0]) - return model.duration + return _as_numeric_duration(model.duration) @classmethod def _cut_protocol(cls, model, continuous_duration): @@ -145,7 +74,7 @@ def _cut_protocol(cls, model, continuous_duration): pairs = list( map( partial(cls._cut_protocol, continuous_duration=continuous_duration), - model.sequence, + model.pulses, ) ) @@ -154,158 +83,164 @@ def _cut_protocol(cls, model, continuous_duration): remainder = [r for r in map(lambda x: x[1], pairs) if r is not None] if remainder: - return cut, ParallelProtocol(sequence=remainder) + return cut, ParallelProtocol(pulses=remainder) return cut, None - if isinstance(model, SequentialProtocol): + if isinstance(model, SerialProtocol): cut, remainder = cls._cut_protocol( - model.sequence[0], continuous_duration=continuous_duration + model.pulses[0], continuous_duration=continuous_duration ) if remainder: - return cut, SequentialProtocol( - sequence=[remainder, *model.sequence[1:]] + return cut, SerialProtocol( + pulses=[remainder, *model.pulses[1:]] ) - if model.sequence[1:]: - return cut, SequentialProtocol(sequence=model.sequence[1:]) + if model.pulses[1:]: + return cut, SerialProtocol(pulses=model.pulses[1:]) return cut, None + total = _as_numeric_duration(model.duration) cut = model.model_copy(deep=True) - if cut.duration == continuous_duration: + + if total == continuous_duration: return [cut], None - cut.duration = continuous_duration - + cut.duration = MathNum(value=continuous_duration) remainder = model.model_copy(deep=True) - remainder.duration = remainder.duration - continuous_duration + remainder.duration = MathSub( + expr1=model.duration, + expr2=MathNum(value=continuous_duration), + ) return [cut], remainder def map_ParallelProtocol(self, model): - sequence = model.sequence + statements = model.pulses protocols = [] - while sequence: - continuous_duration = min(map(self._get_continuous_duration, sequence)) + while statements: + continuous_duration = min(map(self._get_continuous_duration, statements)) pairs = list( map( partial( self._cut_protocol, continuous_duration=continuous_duration ), - sequence, + statements, ) ) protocols.append( ParallelProtocol( - sequence=reduce(lambda x, y: x + y, map(lambda x: x[0], pairs)) + pulses=reduce(lambda x, y: x + y, map(lambda x: x[0], pairs)) ) ) - sequence = [r for r in map(lambda x: x[1], pairs) if r is not None] + statements = [r for r in map(lambda x: x[1], pairs) if r is not None] - return SequentialProtocol(sequence=protocols) + return SerialProtocol(pulses=protocols) - def map_SequentialProtocol(self, model): - if len(model.sequence) == 1: - return model.sequence[0] + def map_SerialProtocol(self, model): + if len(model.pulses) == 1: + return model.pulses[0] - new_sequence = [] - for subprotocol in model.sequence: - if isinstance(subprotocol, SequentialProtocol): - new_sequence.extend( + new_statements = [] + for subprotocol in model.pulses: + if isinstance(subprotocol, SerialProtocol): + new_statements.extend( list( map( lambda x: x if isinstance(x, ParallelProtocol) - else ParallelProtocol(sequence=[x]), - subprotocol.sequence, + else ParallelProtocol(pulses=[x]), + subprotocol.pulses, ) ) ) elif isinstance(subprotocol, ParallelProtocol): - new_sequence.append(subprotocol) + new_statements.append(subprotocol) else: - new_sequence.append(ParallelProtocol(sequence=[subprotocol])) - return model.__class__(sequence=new_sequence) + new_statements.append(ParallelProtocol(pulses=[subprotocol])) + return model.__class__(pulses=new_statements) def map_Pulse(self, model): - return SequentialProtocol(sequence=[model]) - - -class ResolveRelativeTime(RewriteRule): - """ - Handles conversion of relative time to absolute time. - - Args: - model (AtomicCircuit): The rule only acts on [`AtomicCircuit`][oqd_core.interface.atomic.AtomicCircuit] objects. - - Returns: - model (AtomicCircuit): - - Assumptions: - None - """ - - def __init__(self): - super().__init__() - - def map_AtomicCircuit(self, model): - protocol = Post( - SubstituteMathVar( - variable=MathVar(name="s"), substitution=MathVar(name="t") - ) - )(model.protocol) - - return model.__class__(system=model.system, protocol=protocol) - - @classmethod - def _get_duration(cls, model): - if isinstance(model, SequentialProtocol): - return reduce( - lambda x, y: x + y, - [cls._get_duration(p) for p in model.sequence], - ) - if isinstance(model, ParallelProtocol): - if len(model.sequence) == 1: - return cls._get_duration(model.sequence[0]) - - return max( - *[cls._get_duration(p) for p in model.sequence], - ) - return model.duration - - def map_SequentialProtocol(self, model): - current_time = 0 - - new_sequence = [] - for p in model.sequence: - duration = self._get_duration(p) - - new_p = Post( - SubstituteMathVar( - variable=MathVar(name="s"), - substitution=MathVar(name="s") - current_time, - ) - )(p) - new_sequence.append(new_p) - - current_time += duration - - return model.__class__(sequence=new_sequence) + return SerialProtocol(pulses=[model]) + + def map_Declaration(self, model: Declaration): + pass + + def map_IfElse(self, model: IfElse): + pass + + def map_While(self, model: While): + pass + + +# class ResolveRelativeTime(RewriteRule): +# """ +# Handles conversion of relative time to absolute time. + +# Args: +# model (AtomicCircuit): The rule only acts on [`AtomicCircuit`][oqd_core.interface.atomic.AtomicCircuit] objects. + +# Returns: +# model (AtomicCircuit): + +# Assumptions: +# None +# """ + +# def __init__(self): +# super().__init__() + +# def map_AtomicCircuit(self, model): +# protocol = Post( +# SubstituteMathVar( +# variable=MathVar(name="#s"), substitution=MathVar(name="#t") +# ) +# )(model.statements) + +# return model.__class__(statements=protocol) + +# @classmethod +# def _get_duration(cls, model): +# if isinstance(model, SerialProtocol): +# return reduce( +# lambda x, y: x + y, +# [cls._get_duration(p) for p in model.pulses], +# ) +# if isinstance(model, ParallelProtocol): +# if len(model.pulses) == 1: +# return cls._get_duration(model.pulses[0]) + +# return max( +# *[cls._get_duration(p) for p in model.pulses], +# ) +# return model.duration + +# def map_SerialProtocol(self, model): +# current_time = 0 + +# new_statements = [] +# for p in model.pulses: +# duration = self._get_duration(p) + +# new_p = Post( +# SubstituteMathVar( +# variable=MathVar(name="#s"), +# substitution=MathVar(name="#s") - current_time, +# ) +# )(p) +# new_statements.append(new_p) + +# current_time += duration + +# return model.__class__(pulses=new_statements) ######################################################################################## -unroll_label_pass = Chain( - Pre(UnrollLevelLabel()), - Pre(UnrollTransitionLabel()), -) -""" -Pass that unrolls the references to levels and transitions -""" def canonicalize_atomic_circuit_factory(): @@ -313,7 +248,6 @@ def canonicalize_atomic_circuit_factory(): Factory for creating a pass for canonicalizing an atomic circuit. """ return Chain( - unroll_label_pass, - Post(ResolveRelativeTime()), + # Post(ResolveRelativeTime()), Post(ResolveNestedProtocol()), ) diff --git a/src/oqd_core/compiler/atomic/cfg_passes/__init__.py b/src/oqd_core/compiler/atomic/cfg_passes/__init__.py new file mode 100644 index 00000000..ea701c32 --- /dev/null +++ b/src/oqd_core/compiler/atomic/cfg_passes/__init__.py @@ -0,0 +1,6 @@ +from .walk import canonicalize_math_block, canonicalize_math_cfg, iter_stmt_blocks +__all__ = [ + "iter_stmt_blocks", + "canonicalize_math_block", + "canonicalize_math_cfg", +] diff --git a/src/oqd_core/compiler/atomic/cfg_passes/walk.py b/src/oqd_core/compiler/atomic/cfg_passes/walk.py new file mode 100644 index 00000000..3762710f --- /dev/null +++ b/src/oqd_core/compiler/atomic/cfg_passes/walk.py @@ -0,0 +1,89 @@ +# Copyright 2024-2025 Open Quantum Design + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from __future__ import annotations + + +from oqd_core.analysis.utils.control_flow import Block, ControlFlowGraph +from oqd_core.compiler.atomic.math.passes import canonicalize_math_expr +from oqd_core.interface.atomic import ( + AtomicList, + Beam, + Declaration, + ParallelProtocol, + Pulse, + SerialProtocol, +) +from oqd_core.interface.atomic.expr import AtomicExpr + +def iter_stmt_blocks(cfg: ControlFlowGraph): + for node_id, block in cfg.blocks.items(): + if isinstance(block.stmt, str): + continue + yield node_id, block + +def canonicalize_expr(expr): + if isinstance(expr, AtomicExpr): + return canonicalize_math_expr(expr) + return expr + +def canonicalize_beam(beam: Beam) -> Beam: + beam.frequency = canonicalize_expr(beam.frequency) + beam.rabi = canonicalize_expr(beam.rabi) + beam.phase = canonicalize_expr(beam.phase) + beam.polarization = canonicalize_expr(beam.polarization) + beam.wavevector = canonicalize_expr(beam.wavevector) + return beam + +def canonicalize_atomic_list(values: AtomicList) -> AtomicList: + values.values = [canonicalize_expr(v) for v in values.values] + return values + +def canonicalize_math_block(block: Block): + stmt = block.stmt + + if block.kind == "branch": + block.stmt = canonicalize_expr(stmt) + return + + if isinstance(stmt, Pulse): + stmt.duration = canonicalize_expr(stmt.duration) + stmt.target = canonicalize_expr(stmt.target) + stmt.measured = canonicalize_expr(stmt.measured) + if isinstance(stmt.beam, Beam): + stmt.beam = canonicalize_beam(stmt.beam) + else: + stmt.beam = canonicalize_expr(stmt.beam) + return + + if isinstance(stmt, Declaration): + if isinstance(stmt.value, Beam): + stmt.value = canonicalize_beam(stmt.value) + elif isinstance(stmt.value, AtomicList): + stmt.value = canonicalize_atomic_list(stmt.value) + else: + stmt.value = canonicalize_expr(stmt.value) + return + + if isinstance(stmt, (ParallelProtocol, SerialProtocol)): + return + + +def canonicalize_math_cfg(cfg: ControlFlowGraph): + for _, block in iter_stmt_blocks(cfg): + canonicalize_math_block(block) + return cfg + + diff --git a/src/oqd_core/compiler/atomic/error.py b/src/oqd_core/compiler/atomic/error.py new file mode 100644 index 00000000..3ad24ab1 --- /dev/null +++ b/src/oqd_core/compiler/atomic/error.py @@ -0,0 +1,27 @@ +# Copyright 2024-2025 Open Quantum Design + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +__all__ = [ + "AtomicCompilerError", +] + +######################################################################################## + + +class AtomicCompilerError(Exception): + """ + Error class for Atomic compiler + """ + + pass diff --git a/src/oqd_core/compiler/atomic/math/rules.py b/src/oqd_core/compiler/atomic/math/rules.py index 84db333b..ab2012ce 100644 --- a/src/oqd_core/compiler/atomic/math/rules.py +++ b/src/oqd_core/compiler/atomic/math/rules.py @@ -19,7 +19,7 @@ from pydantic import TypeAdapter, ValidationError from oqd_core.interface.atomic import ( - ConstantMathExpr, + # CastMathExpr, MathAdd, MathBinaryOp, MathDiv, @@ -508,7 +508,7 @@ def map_MathImag(self, model): def map_MathExpr(self, model): try: - TypeAdapter(ConstantMathExpr).validate_python(model) + # TypeAdapter(CastMathExpr).validate_python(model) value = Post(EvaluateMathExpr())(model) diff --git a/tests/test_atomic/test_beam.py b/tests/test_atomic/test_beam.py index 76525ba9..884761b8 100644 --- a/tests/test_atomic/test_beam.py +++ b/tests/test_atomic/test_beam.py @@ -15,179 +15,149 @@ import numpy as np import pytest -from oqd_core.interface.atomic import Beam -from oqd_core.interface.atomic.species import Yb171IIBuilder -from oqd_core.interface.math import MathStr +from oqd_core.interface.atomic import ( + Beam, + MathVar, + AtomicList, + MathFunc, + MathMul, + MathImag, + MathNum, +) ######################################################################################## - -@pytest.fixture -def ion(): - return Yb171IIBuilder().build() - +T = MathVar(name="#t") +A = MathVar(name="#A") +DEFAULT_WV = AtomicList(values=[1, 0, 0]) +DEFAULT_POL = AtomicList(values=[0, 0, 1]) class TestBeam: - def test_zero_beam(self, ion): + def test_zero_beam(self): Beam( - transition=ion.transitions[0], - detuning=0, + frequency=0, rabi=0, phase=0, - polarization=[0, 0, 1], - wavevector=[1, 0, 0], - target=0, + polarization=DEFAULT_POL, + wavevector=DEFAULT_WV, ) - def test_constant_beam(self, ion): + def test_constant_beam(self): Beam( - transition=ion.transitions[0], - detuning=0, + frequency=0, rabi=1, phase=np.pi, - polarization=[0, 0, 1], - wavevector=[1, 0, 0], - target=0, + polarization=DEFAULT_POL, + wavevector=DEFAULT_WV, ) @pytest.mark.parametrize( "rabi", [ - MathStr(string="t"), - MathStr(string="sin(t)"), - MathStr(string="A*sin(t)"), + T, + MathFunc(func="sin", expr=T), + MathMul(expr1=A, expr2=MathFunc(func="sin", expr=T)), ], ) - def test_time_dependent_rabi(self, ion, rabi): + def test_time_dependent_rabi(self, rabi): Beam( - transition=ion.transitions[0], - detuning=0, + frequency=0, rabi=rabi, phase=0, - polarization=[0, 0, 1], - wavevector=[1, 0, 0], - target=0, - ) - - @pytest.mark.parametrize( - "detuning", - [ - MathStr(string="t"), - MathStr(string="sin(t)"), - MathStr(string="A*sin(t)"), - ], - ) - def test_time_dependent_detuning(self, ion, detuning): - Beam( - transition=ion.transitions[0], - detuning=detuning, - rabi=1, - phase=0, - polarization=[0, 0, 1], - wavevector=[1, 0, 0], - target=0, + polarization=DEFAULT_POL, + wavevector=DEFAULT_WV, ) @pytest.mark.parametrize( "phase", [ - MathStr(string="t"), - MathStr(string="sin(t)"), - MathStr(string="A*sin(t)"), + T, + MathFunc(func="sin", expr=T), + MathMul(expr1=A, expr2=MathFunc(func="sin", expr=T)), ], ) - def test_time_dependent_phase(self, ion, phase): + def test_time_dependent_phase(self, phase): Beam( - transition=ion.transitions[0], - detuning=0, + frequency=0, rabi=1, phase=phase, - polarization=[0, 0, 1], - wavevector=[1, 0, 0], - target=0, + polarization=DEFAULT_POL, + wavevector=DEFAULT_WV, ) @pytest.mark.parametrize( "polarization", [ - [1, 0, 0], - [0, 1, 0], - [0, 0, 1], - [1, 1, 0], - [1, 1j, 0], - [1, -1j, 0], - [1, -1j, 0], - [0, 0, MathStr(string="exp(1j*3.1415926535)")], + AtomicList(values=[1, 0, 0]), + AtomicList(values=[0, 1, 0]), + AtomicList(values=[0, 0, 1]), + AtomicList(values=[1, 1, 0]), + AtomicList(values=[1, 1j, 0]), + AtomicList(values=[1, -1j, 0]), + AtomicList(values=[1, -1j, 0]), + AtomicList(values=[0, 0, MathFunc(func="exp", expr=MathMul(expr1=MathImag(), expr2=MathNum(value=3.1415926535)),),]), ], ) - def test_polarization(self, ion, polarization): + def test_polarization(self, polarization): Beam( - transition=ion.transitions[0], - detuning=0, + frequency=0, rabi=1, phase=0, polarization=polarization, - wavevector=[1, 0, 0], - target=0, + wavevector=DEFAULT_WV, ) @pytest.mark.xfail @pytest.mark.parametrize( "polarization", [ - [0, 0, MathStr(string="t")], - [0, 0, MathStr(string="sin(t)")], - [0, 0, MathStr(string="A*sin(t)")], + AtomicList(values=[0, 0, T]), + AtomicList(values=[0, 0, MathFunc(func="sin", expr=T)]), + AtomicList(values=[0, 0, MathMul(expr1=A, expr2=MathFunc(func="sin", expr=T))]), ], ) - def test_non_constant_polarization(self, ion, polarization): + def test_non_constant_polarization(self, polarization): Beam( - transition=ion.transitions[0], - detuning=0, + frequency=0, rabi=1, phase=0, polarization=polarization, - wavevector=[1, 0, 0], - target=0, + wavevector=DEFAULT_WV, ) @pytest.mark.parametrize( "wavevector", [ - [1, 0, 0], - [0, 1, 0], - [0, 0, 1], - [1, 1, 0], - [0, 0, MathStr(string="exp(1j*3.1415926535)")], + AtomicList(values=[1, 0, 0]), + AtomicList(values=[0, 1, 0]), + AtomicList(values=[0, 0, 1]), + AtomicList(values=[1, 1, 0]), + AtomicList(values=[0, 0, MathFunc(func="exp", expr=MathMul(expr1=MathImag(), expr2=MathNum(value=3.1415926535)),),]), ], ) - def test_wavevector(self, ion, wavevector): + def test_wavevector(self, wavevector): Beam( - transition=ion.transitions[0], - detuning=0, + frequency=0, rabi=1, phase=0, - polarization=[0, 0, 1], + polarization=DEFAULT_POL, wavevector=wavevector, - target=0, ) @pytest.mark.xfail @pytest.mark.parametrize( "wavevector", [ - [0, 0, MathStr(string="t")], - [0, 0, MathStr(string="sin(t)")], - [0, 0, MathStr(string="A*sin(t)")], + AtomicList(values=[0, 0, T]), + AtomicList(values=[0, 0, MathFunc(func="sin", expr=T)]), + AtomicList(values=[0, 0, MathMul(expr1=A, expr2=MathFunc(func="sin", expr=T))]), ], ) - def test_non_constant_wavevector(self, ion, wavevector): + def test_non_constant_wavevector(self, wavevector): Beam( - transition=ion.transitions[0], - detuning=0, + frequency=0, rabi=1, phase=0, - polarization=[0, 0, 1], + polarization=DEFAULT_POL, wavevector=wavevector, - target=0, ) diff --git a/tests/test_atomic/test_unroll.py b/tests/test_atomic/test_unroll.py deleted file mode 100644 index 67a27746..00000000 --- a/tests/test_atomic/test_unroll.py +++ /dev/null @@ -1,265 +0,0 @@ -# Copyright 2024-2025 Open Quantum Design - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import numpy as np - -from oqd_core.compiler.atomic.canonicalize import unroll_label_pass - -######################################################################################## -from oqd_core.interface.atomic import ( - AtomicCircuit, - Beam, - Ion, - Level, - Pulse, - SequentialProtocol, - System, - Transition, -) - -######################################################################################## - - -def test_unroll_string_labels(): - downstate = Level( - label="q0", - principal=6, - spin=1 / 2, - orbital=0, - nuclear=1 / 2, - spin_orbital=1 / 2, - spin_orbital_nuclear=0, - spin_orbital_nuclear_magnetization=0, - energy=2 * np.pi * 0, - ) - estate = Level( - label="e0", - principal=5, - spin=1 / 2, - orbital=1, - nuclear=1 / 2, - spin_orbital=1 / 2, - spin_orbital_nuclear=1, - spin_orbital_nuclear_magnetization=-1, - energy=2 * np.pi * 100, - ) - - transitions = [ - Transition( - label="q0->e0", - level1="q0", - level2="e0", - einsteinA=1, - multipole="E1", - ), - ] - - ion = Ion( - mass=171, - charge=1, - position=[0, 0, 0], - levels=[downstate, estate], - transitions=transitions, - ) - - system = System( - ions=[ - ion, - ], - modes=[], - ) - - beam = Beam( - transition="q0->e0", - rabi=2 * np.pi * 1, - detuning=0, - phase=0, - polarization=[1, 0, 0], - wavevector=[0, 1, 0], - target=0, - ) - - protocol = SequentialProtocol( - sequence=[ - Pulse(beam=beam, duration=1), - ] - ) - - atomic_circuit = AtomicCircuit(system=system, protocol=protocol) - - ######################################################################################## - - unrolled_transitions = [ - Transition( - label="q0->e0", - level1=downstate, - level2=estate, - einsteinA=1, - multipole="E1", - ), - ] - - unrolled_ion = Ion( - mass=171, - charge=1, - position=[0, 0, 0], - levels=[downstate, estate], - transitions=unrolled_transitions, - ) - - unrolled_system = System( - ions=[ - unrolled_ion, - ], - modes=[], - ) - - unrolled_beam = Beam( - transition=unrolled_transitions[0], - rabi=2 * np.pi * 1, - detuning=0, - phase=0, - polarization=[1, 0, 0], - wavevector=[0, 1, 0], - target=0, - ) - - unrolled_protocol = SequentialProtocol( - sequence=[ - Pulse(beam=unrolled_beam, duration=1), - ] - ) - - unrolled_atomic_circuit = AtomicCircuit( - system=unrolled_system, protocol=unrolled_protocol - ) - - assert unroll_label_pass(atomic_circuit) == unrolled_atomic_circuit - - -def test_unroll_tuple_labels(): - downstate = Level( - label="q0", - principal=6, - spin=1 / 2, - orbital=0, - nuclear=1 / 2, - spin_orbital=1 / 2, - spin_orbital_nuclear=0, - spin_orbital_nuclear_magnetization=0, - energy=2 * np.pi * 0, - ) - estate = Level( - label="e0", - principal=5, - spin=1 / 2, - orbital=1, - nuclear=1 / 2, - spin_orbital=1 / 2, - spin_orbital_nuclear=1, - spin_orbital_nuclear_magnetization=-1, - energy=2 * np.pi * 100, - ) - - transitions = [ - Transition( - label="q0->e0", - level1="q0", - level2="e0", - einsteinA=1, - multipole="E1", - ), - ] - - ion = Ion( - mass=171, - charge=1, - position=[0, 0, 0], - levels=[downstate, estate], - transitions=transitions, - ) - - system = System( - ions=[ - ion, - ], - modes=[], - ) - - beam = Beam( - transition=("q0->e0", 0), - rabi=2 * np.pi * 1, - detuning=0, - phase=0, - polarization=[1, 0, 0], - wavevector=[0, 1, 0], - target=0, - ) - - protocol = SequentialProtocol( - sequence=[ - Pulse(beam=beam, duration=1), - ] - ) - - atomic_circuit = AtomicCircuit(system=system, protocol=protocol) - - ######################################################################################## - - unrolled_transitions = [ - Transition( - label="q0->e0", - level1=downstate, - level2=estate, - einsteinA=1, - multipole="E1", - ), - ] - - unrolled_ion = Ion( - mass=171, - charge=1, - position=[0, 0, 0], - levels=[downstate, estate], - transitions=unrolled_transitions, - ) - - unrolled_system = System( - ions=[ - unrolled_ion, - ], - modes=[], - ) - - unrolled_beam = Beam( - transition=unrolled_transitions[0], - rabi=2 * np.pi * 1, - detuning=0, - phase=0, - polarization=[1, 0, 0], - wavevector=[0, 1, 0], - target=0, - ) - - unrolled_protocol = SequentialProtocol( - sequence=[ - Pulse(beam=unrolled_beam, duration=1), - ] - ) - - unrolled_atomic_circuit = AtomicCircuit( - system=unrolled_system, protocol=unrolled_protocol - ) - - assert unroll_label_pass(atomic_circuit) == unrolled_atomic_circuit diff --git a/tests/test_serialization.py b/tests/test_serialization.py deleted file mode 100644 index 14d68730..00000000 --- a/tests/test_serialization.py +++ /dev/null @@ -1,257 +0,0 @@ -# Copyright 2024-2025 Open Quantum Design - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import numpy as np -import pytest - -from oqd_core.interface.analog import ( - AnalogCircuit, - AnalogGate, - Annihilation, - Creation, - Identity, - PauliI, - PauliX, - PauliY, - PauliZ, -) -from oqd_core.interface.atomic import ( - AtomicCircuit, - Beam, - Ion, - Level, - ParallelProtocol, - Phonon, - Pulse, - SequentialProtocol, - System, - Transition, -) - -######################################################################################## -from oqd_core.interface.math import MathStr - -######################################################################################## - -X, Y, Z, PI, A, C, LI = ( - PauliX(), - PauliY(), - PauliZ(), - PauliI(), - Annihilation(), - Creation(), - Identity(), -) - - -Hx = AnalogGate(hamiltonian=-(np.pi / 4) * X) -analog_circuit = AnalogCircuit() -analog_circuit.evolve(duration=1, gate=Hx) -analog_circuit.evolve(duration=1, gate=Hx) -analog_circuit.evolve(duration=1, gate=Hx) -analog_circuit.measure() - -######################################################################################## - -downstate = Level( - label="q0", - principal=6, - spin=1 / 2, - orbital=0, - nuclear=1 / 2, - spin_orbital=1 / 2, - spin_orbital_nuclear=0, - spin_orbital_nuclear_magnetization=0, - energy=2 * np.pi * 0, -) -upstate = Level( - label="q1", - principal=6, - spin=1 / 2, - orbital=0, - nuclear=1 / 2, - spin_orbital=1 / 2, - spin_orbital_nuclear=1, - spin_orbital_nuclear_magnetization=0, - energy=2 * np.pi * 10, -) -estate = Level( - label="e0", - principal=5, - spin=1 / 2, - orbital=1, - nuclear=1 / 2, - spin_orbital=1 / 2, - spin_orbital_nuclear=1, - spin_orbital_nuclear_magnetization=-1, - energy=2 * np.pi * 100, -) -estate2 = Level( - label="e1", - principal=5, - spin=1 / 2, - orbital=1, - nuclear=1 / 2, - spin_orbital=1 / 2, - spin_orbital_nuclear=1, - spin_orbital_nuclear_magnetization=1, - energy=2 * np.pi * 110, -) - -transitions = [ - Transition( - label="q0->e0", - level1="q0", - level2="e0", - einsteinA=1, - multipole="E1", - ), - Transition( - label="q0->e1", - level1="q0", - level2="e1", - einsteinA=1, - multipole="E1", - ), - Transition( - label="q1->e0", - level1="q1", - level2="e0", - einsteinA=1, - multipole="E1", - ), - Transition( - label="q1->e1", - level1="q1", - level2="e1", - einsteinA=1, - multipole="E1", - ), -] - -ion = Ion( - mass=171, - charge=1, - position=[0, 0, 0], - levels=[downstate, upstate, estate, estate2], - transitions=transitions, -) - -COM_x = Phonon(energy=0.1, eigenvector=[1, 0, 0]) - -system = System( - ions=[ - ion, - ], - modes=[ - COM_x, - ], -) - -beam = Beam( - transition="q0->e0", - rabi=2 * np.pi * 1, - detuning=0, - phase=0, - polarization=[1, 0, 0], - wavevector=[0, 1, 0], - target=0, -) - -beam2 = Beam( - transition="q0->e0", - rabi=2 * np.pi * 5, - detuning=2 * np.pi * 25, - phase=0, - polarization=[1, 0, 0], - wavevector=[0, 1, 0], - target=0, -) - -beam3 = Beam( - transition="q1->e0", - rabi=2 * np.pi * 5, - detuning=2 * np.pi * 25, - phase=0, - polarization=[1, 0, 0], - wavevector=[0, 1, 0], - target=0, -) - -protocol1 = SequentialProtocol( - sequence=[ - Pulse(beam=beam, duration=10), - ] -) -protocol2 = ParallelProtocol( - sequence=[ - Pulse(beam=beam2, duration=10), - Pulse(beam=beam3, duration=10), - ] -) - -protocol = SequentialProtocol( - sequence=[ - protocol1, - protocol2, - ] -) - -atomic_circuit = AtomicCircuit(system=system, protocol=protocol) - -######################################################################################## - - -@pytest.mark.parametrize( - "model", - [ - MathStr(string="1"), - MathStr(string="t"), - MathStr(string="1+2"), - MathStr(string="1*2"), - MathStr(string="1**2"), - MathStr(string="sin(t)"), - MathStr(string="exp(t)"), - X, - Y @ Y @ X, - X + Y + Z + PI, - A + C + LI, - Hx, - analog_circuit, - downstate, - upstate, - estate, - estate2, - transitions[0], - transitions[1], - transitions[2], - transitions[3], - ion, - COM_x, - system, - beam, - beam2, - beam3, - Pulse(beam=beam, duration=10), - Pulse(beam=beam2, duration=10), - Pulse(beam=beam3, duration=10), - protocol1, - protocol2, - protocol, - atomic_circuit, - ], -) -def test_serialize_deserialize(model): - assert model.model_validate(model.model_dump()) == model - assert model.model_validate_json(model.model_dump_json()) == model