Skip to content

Commit

Permalink
integrate tweedledum xag_synth
Browse files Browse the repository at this point in the history
  • Loading branch information
dakk committed Oct 26, 2023
1 parent 031fb34 commit e9c2a02
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 40 deletions.
3 changes: 2 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@
- [x] Remove quantum circuit identities
- [x] For unrolling
- [x] Bitwise xor, or, and
- [ ] Aggregate cascading expressions in for unrolling
- [x] Integrate tweedledum for compilation
- [x] Aggregate cascading expressions in for unrolling

### Week 2: (30 Oct 23)
### Week 3: (6 Nov 23)
Expand Down
8 changes: 4 additions & 4 deletions examples/ex1.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ def f_comp(n: Qint4) -> bool:
gate1 = f1.gate()
gate2 = f2.gate()
gate3 = f3.gate()
qc = QuantumCircuit(max(gate1.num_qubits, gate2.num_qubits) + gate3.num_qubits)
qc = QuantumCircuit(10)
qc.barrier(label="=")
qc.append(gate1, [0, 1, 2, 3, 4, 5])
qc.append(gate1, [0, 1, 2, 3, 4, 5, 6])
qc.barrier(label=">")
qc.append(gate2, [0, 1, 2, 3, 6, 7, 8, 9])
qc.append(gate2, [0, 1, 2, 3, 7, 8])
qc.barrier(label="|")
qc.append(gate3, [5, 9, 10, 11, 12])
qc.append(gate3, [5, 8, 9])
print(qc.decompose().draw("text"))
print(qc.decompose().count_ops())
5 changes: 4 additions & 1 deletion qlasskit/compiler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@
from .multipass import MultipassCompiler
from .poccompiler2 import POCCompiler2
from .poccompiler3 import POCCompiler3
from .tweedledumcompiler import TweedledumCompiler


def to_quantum(name, args, returns, exprs, compiler="poc2"):
def to_quantum(name, args, returns, exprs, compiler="tw"):
if compiler == "multipass":
s = MultipassCompiler()
elif compiler == "poc2":
s = POCCompiler2()
elif compiler == "poc3":
s = POCCompiler3()
elif compiler == "tw":
s = TweedledumCompiler()

circ = s.compile(name, args, returns, exprs)
return circ
2 changes: 1 addition & 1 deletion qlasskit/compiler/poccompiler2.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# 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..
# limitations under the License.

from sympy import Symbol
from sympy.logic import And, Not, Xor
Expand Down
25 changes: 13 additions & 12 deletions qlasskit/compiler/poccompiler3.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# 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..
# limitations under the License.

from sympy import Symbol
from sympy.logic import And, Not, Xor
Expand All @@ -24,7 +24,6 @@
def count_symbol_in_expr(expr, d={}):
for arg in expr.args:
if isinstance(arg, Symbol):
# print(arg)
if arg.name not in d:
d[arg.name] = 0
d[arg.name] += 1
Expand All @@ -36,14 +35,12 @@ def count_symbol_in_expr(expr, d={}):
def count_symbols_in_exprs(exprs):
d = {}
for s, e in exprs:
# print(s,e)
d = count_symbol_in_expr(e, d)
# print(d)
return d


class POCCompiler3(Compiler):
"""POC2 compiler translating an expression list to quantum circuit"""
"""POC3 compiler translating an expression list to quantum circuit"""

def garbage_collect(self, qc):
uncomputed = qc.uncompute()
Expand All @@ -62,13 +59,20 @@ def compile(self, name, args: Args, returns: Arg, exprs: BoolExpList) -> QCircui
self.expqmap = ExpQMap()

for sym, exp in exprs:
print(self.symbol_count)
# print(sym, self._symplify_exp(exp))
iret = self.compile_expr(qc, exp)
# print("iret", iret)
qc.map_qubit(sym, iret, promote=True)

self.garbage_collect(qc)

print(sym, exp)
print(self.symbol_count)
circ_qi = qc.export("circuit", "qiskit")
print(circ_qi.draw("text"))
print()
print()
return qc

def compile_expr(self, qc: QCircuit, expr: Boolean) -> int: # noqa: C901
Expand All @@ -78,6 +82,7 @@ def compile_expr(self, qc: QCircuit, expr: Boolean) -> int: # noqa: C901
return qc[expr.name]

elif expr in self.expqmap:
print(expr, self.expqmap.exp_map)
for s, c in count_symbol_in_expr(expr, {}).items():
self.symbol_count[s] -= c

Expand All @@ -86,17 +91,17 @@ def compile_expr(self, qc: QCircuit, expr: Boolean) -> int: # noqa: C901
elif (
isinstance(expr, Not)
and isinstance(expr.args[0], Symbol)
and self.symbol_count[expr.args[0].name] <= 1
and self.symbol_count[expr.args[0].name] == 1
):
self.symbol_count[expr.args[0].name] = 0
print("thegame")
eret = self.compile_expr(qc, expr.args[0])
qc.x(eret)
self.expqmap[expr] = eret
return eret

elif isinstance(expr, Xor) and any(
[
isinstance(e, Symbol) and self.symbol_count[e.name] <= 1
isinstance(e, Symbol) and self.symbol_count[e.name] == 1
for e in expr.args
]
):
Expand All @@ -112,7 +117,6 @@ def compile_expr(self, qc: QCircuit, expr: Boolean) -> int: # noqa: C901
qc.cx(x, last)

[qc.mark_ancilla(eret) for eret in erets]
self.garbage_collect(qc)
self.expqmap[expr] = last
return last

Expand All @@ -129,7 +133,6 @@ def compile_expr(self, qc: QCircuit, expr: Boolean) -> int: # noqa: C901
qc.cx(eret, fa)
qc.x(fa)
qc.mark_ancilla(eret)
self.garbage_collect(qc)
self.expqmap[expr] = fa

return fa
Expand All @@ -140,7 +143,6 @@ def compile_expr(self, qc: QCircuit, expr: Boolean) -> int: # noqa: C901
qc.barrier("and")
qc.mcx(erets, fa)
[qc.mark_ancilla(eret) for eret in erets]
self.garbage_collect(qc)
self.expqmap[expr] = fa

return fa
Expand All @@ -163,7 +165,6 @@ def compile_expr(self, qc: QCircuit, expr: Boolean) -> int: # noqa: C901
qc.cx(x, fa)

[qc.mark_ancilla(eret) for eret in erets]
self.garbage_collect(qc)

return fa

Expand Down
138 changes: 138 additions & 0 deletions qlasskit/compiler/tweedledumcompiler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Copyright 2023 Davide Gessa

# 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 Dict

from sympy import Symbol
from sympy.logic import And, Not, Or, Xor
from sympy.logic.boolalg import BooleanFalse, BooleanTrue
from tweedledum.classical import LogicNetwork
from tweedledum.ir import Qubit, rotation_angle
from tweedledum.passes import parity_decomp
from tweedledum.synthesis import xag_synth

from .. import QCircuit
from ..ast2logic.typing import Arg, Args, BoolExpList
from . import Compiler


def sympy_to_logic_network( # noqa: C901
name, args: Args, returns: Arg, exprs: BoolExpList
) -> LogicNetwork:
_symbol_table: Dict[str, int] = dict()
_logic_network = LogicNetwork()

def _apply(expr, op):
signals = list(map(visit, expr.args))

prev_res = signals[0]
for s in signals[1:]:
prev_res = getattr(_logic_network, op)(prev_res, s)
return prev_res

def visit(expr):
if isinstance(expr, Symbol):
return _symbol_table[expr.name]

elif isinstance(expr, Not):
signals = list(map(visit, expr.args))
return _logic_network.create_not(signals[0])

elif isinstance(expr, And):
return _apply(expr, "create_and")

elif isinstance(expr, Or):
return _apply(expr, "create_or")

elif isinstance(expr, Xor):
return _apply(expr, "create_xor")

elif isinstance(expr, BooleanTrue):
return _logic_network.get_constant(1)

elif isinstance(expr, BooleanFalse):
return _logic_network.get_constant(0)

else:
raise Exception("not handled")

for arg in args:
for bv in arg.bitvec:
pi = _logic_network.create_pi(bv)
_symbol_table[bv] = pi

for s, e in exprs:
v_signal = visit(e)
_symbol_table[s.name] = v_signal

if s.name[0:4] == "_ret":
_logic_network.create_po(v_signal)

return _logic_network


def twcircuit_to_qcircuit(twc):
pass


class TweedledumCompiler(Compiler):
"""Compile using tweedledum synthesis library"""

def compile(self, name, args: Args, returns: Arg, exprs: BoolExpList) -> QCircuit:
exprs = [(symb, self._symplify_exp(exp)) for symb, exp in exprs]

_logic_network = sympy_to_logic_network(name, args, returns, exprs)

sy = xag_synth(_logic_network)
sy = parity_decomp(sy)
# print(exprs)
# print(sy)

qc = QCircuit(sy.num_qubits(), native=sy)

ri = 0
for arg in args:
for bv in arg.bitvec:
qc[bv] = ri
ri += 1

for bv in returns.bitvec:
qc[bv] = ri
ri += 1

for instruction in sy:
qubits = [
(qubit.uid(), qubit.polarity() == Qubit.Polarity.positive)
for qubit in instruction.qubits()
]
op = instruction.kind().split("std.")[1]
n_ctrls = instruction.num_controls()
angle = rotation_angle(instruction)

if n_ctrls > 0:
qb = []
for u, pol in qubits:
if not pol:
qc.x(u)
qb.append(u)

qc.mctrl_gate(op, qb[0:-1], qb[-1], angle)

for u, pol in qubits:
if not pol:
qc.x(u)
else:
qc.append(op, [qubits[0][0]])

return qc
Loading

0 comments on commit e9c2a02

Please sign in to comment.