From 1beff6a9bdf97b83b61b396fb99b25ae18f7135b Mon Sep 17 00:00:00 2001 From: HaYeong Lee <110161434+glorialeezero@users.noreply.github.com> Date: Fri, 22 Nov 2024 06:30:32 +0000 Subject: [PATCH 1/6] Add child, depth to language --- qupsy/language.py | 188 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 150 insertions(+), 38 deletions(-) diff --git a/qupsy/language.py b/qupsy/language.py index 266d8b6..fc5743f 100644 --- a/qupsy/language.py +++ b/qupsy/language.py @@ -23,9 +23,64 @@ def cost(self) -> int: @property @abstractmethod - def depth(self) -> int: + def children(self) -> list[Aexp]: pass + @property + def depth(self) -> int: + children = self.children + return 1 + ( + max([child.depth for child in children]) if len(children) > 0 else 0 + ) + + @property + def filled(self) -> bool: + for aexp in self.children: + if not aexp.filled: + return False + return True + + def copy(self) -> Aexp: + return self.__class__(*[child.copy() for child in self.children]) + + +class Integer(Aexp): + def __init__(self, value: int) -> None: + self.value = value + + def __str__(self) -> str: + return str(self.value) + + def __repr__(self) -> str: + return f"{self.value}" + + @property + def cost(self) -> int: + return 0 + + @property + def children(self) -> list[Aexp]: + return [] + + +class Var(Aexp): + def __init__(self, name: str) -> None: + self.name = name + + def __str__(self) -> str: + return self.name + + def __repr__(self) -> str: + return f"{self.name}" + + @property + def cost(self) -> int: + return 0 + + @property + def children(self) -> list[Aexp]: + return [] + class HoleAexp(Aexp): def __str__(self) -> str: @@ -39,8 +94,12 @@ def cost(self) -> int: return 3 @property - def depth(self) -> int: - return 1 + def children(self) -> list[Aexp]: + return [] + + @property + def filled(self) -> bool: + return False class Add(Aexp): @@ -62,8 +121,8 @@ def cost(self) -> int: return self.a.cost + self.b.cost + 3 @property - def depth(self) -> int: - return max(self.a.depth, self.b.depth) + 1 + def children(self) -> list[Aexp]: + return [self.a, self.b] class Sub(Aexp): @@ -85,8 +144,8 @@ def cost(self) -> int: return self.a.cost + self.b.cost + 3 @property - def depth(self) -> int: - return max(self.a.depth, self.b.depth) + 1 + def chilren(self) -> list[Aexp]: + return [self.a, self.b] class Mul(Aexp): @@ -108,8 +167,8 @@ def cost(self) -> int: return self.a.cost + self.b.cost + 3 @property - def depth(self) -> int: - return max(self.a.depth, self.b.depth) + 1 + def children(self) -> list[Aexp]: + return [self.a, self.b] class Div(Aexp): @@ -131,8 +190,8 @@ def cost(self) -> int: return self.a.cost + self.b.cost + 3 @property - def depth(self) -> int: - return max(self.a.depth, self.b.depth) + 1 + def children(self) -> list[Aexp]: + return [self.a, self.b] class Gate(ABC): @@ -151,9 +210,26 @@ def cost(self) -> int: @property @abstractmethod - def depth(self) -> int: + def children(self) -> list[Aexp]: pass + @property + def depth(self) -> int: + children = self.children + return 1 + ( + max([child.depth for child in children]) if len(children) > 0 else 0 + ) + + @property + def filled(self) -> bool: + for aexp in self.children: + if not aexp.filled: + return False + return True + + def copy(self) -> Gate: + return self.__class__(*[child.copy() for child in self.children]) + class HoleGate(Gate): def __str__(self) -> str: @@ -167,8 +243,12 @@ def cost(self) -> int: return 3 @property - def depth(self) -> int: - return 1 + def children(self) -> list[Aexp]: + return [] + + @property + def filled(self) -> bool: + return False class H(Gate): @@ -188,8 +268,8 @@ def cost(self) -> int: return self.qreg.cost + 2 @property - def depth(self) -> int: - return self.qreg.depth + 1 + def children(self) -> list[Aexp]: + return [self.qreg] class X(Gate): @@ -209,8 +289,8 @@ def cost(self) -> int: return self.qreg.cost + 2 @property - def depth(self) -> int: - return self.qreg.depth + 1 + def children(self) -> list[Aexp]: + return [self.qreg] class Ry(Gate): @@ -236,8 +316,8 @@ def cost(self) -> int: return self.qreg.cost + self.p.cost + self.q.cost + 2 @property - def depth(self) -> int: - return max(self.qreg.depth, self.p.depth, self.q.depth) + 1 + def children(self) -> list[Aexp]: + return [self.qreg, self.p, self.q] class CX(Gate): @@ -259,8 +339,8 @@ def cost(self) -> int: return self.qreg1.cost + self.qreg2.cost + 2 @property - def depth(self) -> int: - return max(self.qreg1.depth, self.qreg2.depth) + 1 + def children(self) -> list[Aexp]: + return [self.qreg1, self.qreg2] class CRy(Gate): @@ -292,8 +372,8 @@ def cost(self) -> int: return self.qreg1.cost + self.qreg2.cost + self.p.cost + self.q.cost + 2 @property - def depth(self) -> int: - return max(self.qreg1.depth, self.qreg2.depth, self.p.depth, self.q.depth) + 1 + def children(self) -> list[Aexp]: + return [self.qreg1, self.qreg2, self.p, self.q] class Cmd(ABC): @@ -313,9 +393,26 @@ def cost(self) -> int: @property @abstractmethod - def depth(self) -> int: + def children(self) -> list[Cmd | Gate | Aexp]: pass + @property + def depth(self) -> int: + children = self.children + return 1 + ( + max([child.depth for child in children]) if len(children) > 0 else 0 + ) + + @property + def filled(self) -> bool: + for child in self.children: + if not child.filled: + return False + return True + + def copy(self) -> Cmd: + return self.__class__(*[child.copy() for child in self.children]) + class HoleCmd(Cmd): def __str__(self) -> str: @@ -329,8 +426,12 @@ def cost(self) -> int: return 5 @property - def depth(self) -> int: - return 1 + def children(self) -> list[Cmd | Gate | Aexp]: + return [] + + @property + def filled(self) -> bool: + return False class SeqCmd(Cmd): @@ -352,8 +453,8 @@ def cost(self) -> int: return self.pre.cost + self.post.cost + 5 @property - def depth(self) -> int: - return max(self.pre.depth, self.post.depth) + 1 + def children(self) -> list[Cmd | Gate | Aexp]: + return [self.pre, self.post] class ForCmd(Cmd): @@ -380,13 +481,16 @@ def __str__(self) -> str: def __repr__(self) -> str: return f"For({self.var!r}, {self.start!r}, {self.end!r}, {self.body!r})" + def copy(self) -> ForCmd: + return ForCmd(self.var, self.start.copy(), self.end.copy(), self.body.copy()) + @property def cost(self) -> int: return self.start.cost + self.end.cost + self.body.cost + 3 @property - def depth(self) -> int: - return self.body.depth + 1 + def children(self) -> list[Cmd | Gate | Aexp]: + return [self.start, self.end, self.body] class GateCmd(Cmd): @@ -406,14 +510,16 @@ def cost(self) -> int: return self.gate.cost @property - def depth(self) -> int: - return self.gate.depth + def children(self) -> list[Cmd | Gate | Aexp]: + return [self.gate] class Pgm: + n: str body: Cmd - def __init__(self, body: Cmd | None = None) -> None: + def __init__(self, n: str, body: Cmd | None = None) -> None: + self.n = n self.body = body or HoleCmd() def __str__(self) -> str: @@ -434,8 +540,14 @@ def depth(self) -> int: return self.body.depth -GATE_MAP: dict[str, type[Gate]] = { - g.__name__: g +ALL_AEXPS: list[type[Aexp]] = [ + a + for a in globals().values() + if isinstance(a, type) and issubclass(a, Aexp) and a != Aexp +] +ALL_GATES: list[type[Gate]] = [ + g for g in globals().values() - if isinstance(g, type) and issubclass(g, Gate) -} + if isinstance(g, type) and issubclass(g, Gate) and g != Gate +] +GATE_MAP: dict[str, type[Gate]] = {g.__name__: g for g in ALL_GATES} From 939ac5d4661cf71cac4bec46e17379c67b4aaf46 Mon Sep 17 00:00:00 2001 From: HaYeong Lee <110161434+glorialeezero@users.noreply.github.com> Date: Fri, 22 Nov 2024 06:30:55 +0000 Subject: [PATCH 2/6] Modify test cases --- tests/test_language.py | 8 ++++---- tests/test_worklist.py | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_language.py b/tests/test_language.py index eb0bf96..ae9a52e 100644 --- a/tests/test_language.py +++ b/tests/test_language.py @@ -2,22 +2,22 @@ def test_pgm_create_with_empty_body(): - pgm = Pgm() + pgm = Pgm("n") assert isinstance(pgm.body, HoleCmd) def test_pgm_to_str(): - pgm = Pgm() + pgm = Pgm("n") assert str(pgm) == str(pgm.body) def test_pgm_cost(): - pgm = Pgm() + pgm = Pgm("n") assert pgm.cost == pgm.body.cost def test_pgm_depth(): - pgm = Pgm() + pgm = Pgm("n") assert pgm.depth == pgm.body.depth diff --git a/tests/test_worklist.py b/tests/test_worklist.py index 6f2f5b4..9134e2e 100644 --- a/tests/test_worklist.py +++ b/tests/test_worklist.py @@ -9,22 +9,22 @@ def test_worklist(): def test_add_same_pgm(): worklist = Worklist() - pgm = Pgm(HoleCmd()) + pgm = Pgm("n", HoleCmd()) worklist.put(pgm, pgm) assert worklist.num_pgm_left() == 1 def test_get_pgm(): worklist = Worklist() - pgm1 = Pgm(HoleCmd()) - pgm2 = Pgm(GateCmd(HoleGate())) + pgm1 = Pgm("n", HoleCmd()) + pgm2 = Pgm("n", GateCmd(HoleGate())) worklist.put(pgm1, pgm2) assert worklist.get() == pgm2 def test_add_same_cost(): worklist = Worklist() - pgm1 = Pgm(SeqCmd(GateCmd(H(HoleAexp())), GateCmd(H(HoleAexp())))) - pgm2 = Pgm(SeqCmd(GateCmd(X(HoleAexp())), GateCmd(X(HoleAexp())))) + pgm1 = Pgm("n", SeqCmd(GateCmd(H(HoleAexp())), GateCmd(H(HoleAexp())))) + pgm2 = Pgm("n", SeqCmd(GateCmd(X(HoleAexp())), GateCmd(X(HoleAexp())))) worklist.put(pgm1, pgm2) assert worklist.get() == pgm1 From c7ceb524d401765985ca447c27ae075a0a7ecb6c Mon Sep 17 00:00:00 2001 From: HaYeong Lee <110161434+glorialeezero@users.noreply.github.com> Date: Fri, 22 Nov 2024 06:31:06 +0000 Subject: [PATCH 3/6] Add transition --- qupsy/transition.py | 167 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 qupsy/transition.py diff --git a/qupsy/transition.py b/qupsy/transition.py new file mode 100644 index 0000000..4a21bfa --- /dev/null +++ b/qupsy/transition.py @@ -0,0 +1,167 @@ +from qupsy.language import ( + ALL_AEXPS, + ALL_GATES, + Aexp, + Cmd, + ForCmd, + Gate, + GateCmd, + HoleAexp, + HoleCmd, + HoleGate, + Integer, + Pgm, + SeqCmd, + Var, +) + + +class TransitionVisitor: + def __init__(self) -> None: + self.n = "" + self.for_depth = 0 + + def visit_HoleAexp(self, aexp: HoleAexp) -> list[Aexp]: + return ( + [a() for a in ALL_AEXPS if a not in (HoleAexp, Integer, Var)] + + [Integer(i) for i in range(3)] + + [Var(f"i{i}") for i in range(self.for_depth)] + + [Var(self.n)] + ) + + def visit_Aexp(self, aexp: Aexp) -> list[Aexp]: + visitor_name = f"visit_{aexp.__class__.__name__}" + visitor = getattr(self, visitor_name, None) + if visitor is not None: + return visitor(aexp) + + for i, child in enumerate(aexp.children): + if child.filled: + continue + next_children = self.visit_Aexp(child) + pre_args = aexp.children[:i] + post_args = aexp.children[i + 1 :] + ret: list[Aexp] = [] + for next_child in next_children: + ret.append( + aexp.__class__( + *(a.copy() for a in pre_args), + next_child, # type: ignore + *(a.copy() for a in post_args), + ) + ) + + return [] + + def visit_HoleGate(self, gate: HoleGate) -> list[Gate]: + return [g() for g in ALL_GATES if g != HoleGate] + + def visit_Gate(self, gate: Gate) -> list[Gate]: + visitor_name = f"visit_{gate.__class__.__name__}" + visitor = getattr(self, visitor_name, None) + if visitor is not None: + return visitor(gate) + + for i, child in enumerate(gate.children): + if child.filled: + continue + next_children = self.visit_Aexp(child) + pre_args = gate.children[:i] + post_args = gate.children[i + 1 :] + ret: list[Gate] = [] + for next_child in next_children: + ret.append( + gate.__class__( + *(a.copy() for a in pre_args), + next_child, # type: ignore + *(a.copy() for a in post_args), + ) + ) + return [] + + def visit_HoleCmd(self, cmd: HoleCmd) -> list[Cmd]: + return [SeqCmd(), ForCmd(var=f"i{self.for_depth}"), GateCmd()] + + def visit_SeqCmd(self, cmd: SeqCmd) -> list[Cmd]: + if not cmd.pre.filled: + pres = self.visit_Cmd(cmd.pre) + ret: list[Cmd] = [] + for pre in pres: + ret.append(SeqCmd(pre=pre, post=cmd.post.copy())) + return ret + if not cmd.post.filled: + posts = self.visit_Cmd(cmd.post) + ret: list[Cmd] = [] + for post in posts: + ret.append(SeqCmd(pre=cmd.pre.copy(), post=post)) + return ret + return [] + + def visit_ForCmd(self, cmd: ForCmd) -> list[Cmd]: + if not cmd.start.filled: + starts = self.visit_Aexp(cmd.start) + ret: list[Cmd] = [] + for start in starts: + ret.append( + ForCmd( + var=cmd.var, + start=start, + end=cmd.end.copy(), + body=cmd.body.copy(), + ) + ) + return ret + if not cmd.end.filled: + ends = self.visit_Aexp(cmd.end) + ret: list[Cmd] = [] + for end in ends: + ret.append( + ForCmd( + var=cmd.var, + start=cmd.start.copy(), + end=end, + body=cmd.body.copy(), + ) + ) + return ret + if not cmd.body.filled: + self.for_depth += 1 + bodys = self.visit_Cmd(cmd.body) + self.for_depth -= 1 + ret: list[Cmd] = [] + for body in bodys: + ret.append( + ForCmd( + var=cmd.var, + start=cmd.start.copy(), + end=cmd.end.copy(), + body=body, + ) + ) + return ret + return [] + + def visit_GateCmd(self, cmd: GateCmd) -> list[Cmd]: + gates = self.visit_Gate(cmd.gate) + return [GateCmd(gate) for gate in gates] + + def visit_Cmd(self, cmd: Cmd) -> list[Cmd]: + if isinstance(cmd, HoleCmd): + return self.visit_HoleCmd(cmd) + if isinstance(cmd, SeqCmd): + return self.visit_SeqCmd(cmd) + if isinstance(cmd, ForCmd): + return self.visit_ForCmd(cmd) + if isinstance(cmd, GateCmd): + return self.visit_GateCmd(cmd) + raise TypeError(f"Unknown command type: {type(cmd)}") + + def visit(self, pgm: Pgm) -> list[Pgm]: + self.n = pgm.n + bodys = self.visit_Cmd(pgm.body) + return [Pgm(pgm.n, body) for body in bodys] + + +def next(pgm: Pgm) -> list[Pgm]: + visitor = TransitionVisitor() + return visitor.visit(pgm) From 525a5c97a0236a0407790485fb0e13d58aa76407 Mon Sep 17 00:00:00 2001 From: HaYeong Lee <110161434+glorialeezero@users.noreply.github.com> Date: Fri, 22 Nov 2024 06:31:14 +0000 Subject: [PATCH 4/6] Add transition testcases --- tests/test_transition.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/test_transition.py diff --git a/tests/test_transition.py b/tests/test_transition.py new file mode 100644 index 0000000..146b34c --- /dev/null +++ b/tests/test_transition.py @@ -0,0 +1,27 @@ +from qupsy.language import ALL_GATES, Cmd, ForCmd, Gate, GateCmd, Pgm, SeqCmd +from qupsy.transition import next + + +def test_hole_command_becomes_three_different_commands(): + pgm = Pgm("n") + nexts = next(pgm) + assert len(nexts) == 3 + + +def test_transition_holecmd(): + pgm = Pgm("n") + command_types: list[type[Cmd]] = [SeqCmd, ForCmd, GateCmd] + for pgm in next(pgm): + assert type(pgm.body) in command_types + command_types.remove(type(pgm.body)) + assert len(command_types) == 0 + + +def test_transition_gatecmd(): + pgm = Pgm("n", GateCmd()) + gate_types: list[type[Gate]] = ALL_GATES + for pgm in next(pgm): + assert isinstance(pgm.body, GateCmd) + assert type(pgm.body.gate) in gate_types + gate_types.remove(type(pgm.body.gate)) + assert len(gate_types) == 1 From e76776e7f8a6d1aba65499994d2e64a5dccda922 Mon Sep 17 00:00:00 2001 From: HaYeong Lee <110161434+glorialeezero@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:48:29 +0900 Subject: [PATCH 5/6] Modify language function --- qupsy/language.py | 12 +++++++++--- qupsy/transition.py | 11 ++++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/qupsy/language.py b/qupsy/language.py index fc5743f..bbebd49 100644 --- a/qupsy/language.py +++ b/qupsy/language.py @@ -62,6 +62,9 @@ def cost(self) -> int: def children(self) -> list[Aexp]: return [] + def copy(self) -> Integer: + return Integer(self.value) + class Var(Aexp): def __init__(self, name: str) -> None: @@ -81,6 +84,9 @@ def cost(self) -> int: def children(self) -> list[Aexp]: return [] + def copy(self) -> Var: + return Var(self.name) + class HoleAexp(Aexp): def __str__(self) -> str: @@ -144,7 +150,7 @@ def cost(self) -> int: return self.a.cost + self.b.cost + 3 @property - def chilren(self) -> list[Aexp]: + def children(self) -> list[Aexp]: return [self.a, self.b] @@ -446,7 +452,7 @@ def __str__(self) -> str: return f"{self.pre}\n{self.post}" def __repr__(self) -> str: - return f"SeqCmd({self.pre:!r}, {self.post:!r})" + return f"SeqCmd({repr(self.pre)}, {repr(self.post)})" @property def cost(self) -> int: @@ -526,7 +532,7 @@ def __str__(self) -> str: return str(self.body) def __repr__(self) -> str: - return f"Pgm({self.body:!r})" + return f"Pgm({repr(self.body)})" def __lt__(self, other: Pgm) -> bool: return self.cost < other.cost diff --git a/qupsy/transition.py b/qupsy/transition.py index 4a21bfa..52024de 100644 --- a/qupsy/transition.py +++ b/qupsy/transition.py @@ -24,7 +24,7 @@ def __init__(self) -> None: def visit_HoleAexp(self, aexp: HoleAexp) -> list[Aexp]: return ( [a() for a in ALL_AEXPS if a not in (HoleAexp, Integer, Var)] - + [Integer(i) for i in range(3)] + + [Integer(i) for i in range(3)] # 0,1,2 + [Var(f"i{i}") for i in range(self.for_depth)] + [Var(self.n)] ) @@ -77,6 +77,7 @@ def visit_Gate(self, gate: Gate) -> list[Gate]: *(a.copy() for a in post_args), ) ) + return ret return [] def visit_HoleCmd(self, cmd: HoleCmd) -> list[Cmd]: @@ -126,10 +127,10 @@ def visit_ForCmd(self, cmd: ForCmd) -> list[Cmd]: return ret if not cmd.body.filled: self.for_depth += 1 - bodys = self.visit_Cmd(cmd.body) + bodies = self.visit_Cmd(cmd.body) self.for_depth -= 1 ret: list[Cmd] = [] - for body in bodys: + for body in bodies: ret.append( ForCmd( var=cmd.var, @@ -158,8 +159,8 @@ def visit_Cmd(self, cmd: Cmd) -> list[Cmd]: def visit(self, pgm: Pgm) -> list[Pgm]: self.n = pgm.n - bodys = self.visit_Cmd(pgm.body) - return [Pgm(pgm.n, body) for body in bodys] + bodies = self.visit_Cmd(pgm.body) + return [Pgm(pgm.n, body) for body in bodies] def next(pgm: Pgm) -> list[Pgm]: From f48bc1bd9b5e8de13e12a3d00703e0646c63d352 Mon Sep 17 00:00:00 2001 From: HaYeong Lee <110161434+glorialeezero@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:48:47 +0900 Subject: [PATCH 6/6] Add testcases --- tests/test_transition.py | 63 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/tests/test_transition.py b/tests/test_transition.py index 146b34c..21010ec 100644 --- a/tests/test_transition.py +++ b/tests/test_transition.py @@ -1,14 +1,28 @@ -from qupsy.language import ALL_GATES, Cmd, ForCmd, Gate, GateCmd, Pgm, SeqCmd +from qupsy.language import ( + ALL_AEXPS, + ALL_GATES, + CX, + Aexp, + Cmd, + ForCmd, + Gate, + GateCmd, + H, + Integer, + Pgm, + SeqCmd, + Var, +) from qupsy.transition import next -def test_hole_command_becomes_three_different_commands(): +def test_hole_cmd_len(): pgm = Pgm("n") nexts = next(pgm) assert len(nexts) == 3 -def test_transition_holecmd(): +def test_hole_cmd(): pgm = Pgm("n") command_types: list[type[Cmd]] = [SeqCmd, ForCmd, GateCmd] for pgm in next(pgm): @@ -17,11 +31,50 @@ def test_transition_holecmd(): assert len(command_types) == 0 -def test_transition_gatecmd(): +def tests_hole_gate(): pgm = Pgm("n", GateCmd()) - gate_types: list[type[Gate]] = ALL_GATES + gate_types: list[type[Gate]] = ALL_GATES.copy() for pgm in next(pgm): assert isinstance(pgm.body, GateCmd) assert type(pgm.body.gate) in gate_types gate_types.remove(type(pgm.body.gate)) assert len(gate_types) == 1 + + +def test_hole_aexp1(): + pgm = Pgm("n", GateCmd(H())) + aexp_types: list[type[Aexp]] = ALL_AEXPS.copy() + pgms = next(pgm) + for pgm in pgms: + assert isinstance(pgm.body, GateCmd) + assert isinstance(pgm.body.gate, H) + assert type(pgm.body.gate.qreg) in aexp_types + if type(pgm.body.gate.qreg) not in [Integer, Var]: + aexp_types.remove(type(pgm.body.gate.qreg)) + assert len(aexp_types) == 3 + + +def test_hole_aexp2(): + pgm = Pgm("n", GateCmd(CX())) + aexp_types: list[type[Aexp]] = ALL_AEXPS.copy() + pgms = next(pgm) + for pgm in pgms: + assert isinstance(pgm.body, GateCmd) + assert isinstance(pgm.body.gate, CX) + assert type(pgm.body.gate.qreg1) in aexp_types + if type(pgm.body.gate.qreg1) not in [Integer, Var]: + aexp_types.remove(type(pgm.body.gate.qreg1)) + assert len(aexp_types) == 3 + + +def test_hole_aexp3(): + pgm = Pgm("n", GateCmd(CX(Integer(0)))) + aexp_types: list[type[Aexp]] = ALL_AEXPS.copy() + pgms = next(pgm) + for pgm in pgms: + assert isinstance(pgm.body, GateCmd) + assert isinstance(pgm.body.gate, CX) + assert type(pgm.body.gate.qreg2) in aexp_types + if type(pgm.body.gate.qreg2) not in [Integer, Var]: + aexp_types.remove(type(pgm.body.gate.qreg2)) + assert len(aexp_types) == 3