Skip to content

Commit

Permalink
bitwise operations binop
Browse files Browse the repository at this point in the history
  • Loading branch information
dakk committed Oct 24, 2023
1 parent 3e865ed commit 7c32d47
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 37 deletions.
17 changes: 13 additions & 4 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
- [x] Extensible type system
- [x] Builtin functions: max(), min(), len()
- [x] Function call (to builtin)
- [x] Int arithmetic: +
- [x] Int arithmetic: +, -
- [x] Qtype: bitwise not
- [x] Qtype: shift right / left
- [x] Int: subtraction
Expand All @@ -62,10 +62,14 @@
- [x] Remove unneccessary expressions
- [x] Remove quantum circuit identities
- [x] For unrolling
- [x] Bitwise xor, or, and
- [ ] Aggregate cascading expressions in for unrolling

### Week 2: (30 Oct 23)
### Week 3: (6 Nov 23)

- [ ] Slideshow for UF midterm

### Week 4: (13 Nov 23)

## Month 3:
Expand Down Expand Up @@ -93,13 +97,13 @@

- [ ] Publish doc on github
- [ ] Inner function
- [ ] Int arithmetic expressions (*, /)
- [ ] Int arithmetic expressions (*, /, mod)
- [ ] Parametrized qlassf
- [ ] Lambda
- [ ] FixedList type
- [ ] Builtin functions: map, count
- [ ] Ast2logic: fixed size loops unrolling
- [ ] Builtin functions: sum(), all(),
- [ ] Builtin functions: sum(), all(), any()
- [ ] First beta release

### Language support
Expand Down Expand Up @@ -135,4 +139,9 @@
### Tools

- [ ] py2qasm tool
- [ ] py2boolexp tool
- [ ] py2boolexp tool


### Experiments

- [ ] Logic2FPGA backend
2 changes: 1 addition & 1 deletion qlasskit/ast2ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def visit_Assign(self, node):

# TODO: support unrolling tuple
# TODO: if value is not self referencing, we can skip this (ie: a = b + 1)

# Reassigning an already present variable (use a temp variable)
if was_known and not isinstance(node.value, ast.Constant):
new_targ = ast.Name(id=f"__{node.targets[0].id}", ctx=ast.Load())
Expand Down
5 changes: 2 additions & 3 deletions qlasskit/ast2logic/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ def __init__(self) -> None:

for t in BUILTIN_TYPES:
self.bind_type((t.__name__, t)) # type: ignore



def __repr__(self):
return str((self.bindings, self.types))

Expand All @@ -56,7 +55,7 @@ def bind(self, bb: Binding, rebind=False):

if rebind:
self.bindings.remove(self[bb.name])

self.bindings.append(bb)

def __contains__(self, key):
Expand Down
19 changes: 16 additions & 3 deletions qlasskit/ast2logic/t_expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from typing import List, Tuple, get_args

from sympy import Symbol
from sympy.logic import ITE, And, Not, Or, false, true
from sympy.logic import ITE, And, Not, Or, Xor, false, true

from ..types import Qbool, Qtype, TExp, const_to_qtype
from . import Env, exceptions
Expand Down Expand Up @@ -205,16 +205,29 @@ def unfold(v_exps, op):

# Binop
elif isinstance(expr, ast.BinOp):
# Sub | Mult | MatMult | Div | Mod | Pow |
# | BitOr | BitXor | BitAnd | FloorDiv
# Sub | Mult | MatMult | Div | Mod | Pow | FloorDiv
# print(ast.dump(expr))
tleft = translate_expression(expr.left, env)
tright = translate_expression(expr.right, env)

if tleft[0] == bool and tright[0] == bool:
if isinstance(expr.op, ast.BitXor):
return bool, Xor(tleft[1], tright[1])
elif isinstance(expr.op, ast.BitAnd):
return bool, And(tleft[1], tright[1])
elif isinstance(expr.op, ast.BitOr):
return bool, Or(tleft[1], tright[1])

if isinstance(expr.op, ast.Add) and hasattr(tleft[0], "add"):
return tleft[0].add(tleft, tright)
elif isinstance(expr.op, ast.Sub) and hasattr(tleft[0], "sub"):
return tleft[0].sub(tleft, tright)
elif isinstance(expr.op, ast.BitXor) and hasattr(tleft[0], "bitwise_xor"):
return tleft[0].bitwise_xor(tleft, tright)
elif isinstance(expr.op, ast.BitAnd) and hasattr(tleft[0], "bitwise_and"):
return tleft[0].bitwise_and(tleft, tright)
elif isinstance(expr.op, ast.BitOr) and hasattr(tleft[0], "bitwise_or"):
return tleft[0].bitwise_or(tleft, tright)
elif (
isinstance(expr.op, ast.LShift)
and hasattr(tleft[0], "shift_left")
Expand Down
45 changes: 25 additions & 20 deletions qlasskit/compiler/poccompiler2.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ def compile(self, name, args: Args, returns: Arg, exprs: BoolExpList) -> QCircui

for arg in args:
for arg_b in arg.bitvec:
qc.add_qubit(arg_b)
qi = qc.add_qubit(arg_b)
# qc.ancilla_lst.add(qi)

self.expqmap = ExpQMap()

Expand All @@ -88,7 +89,7 @@ def compile(self, name, args: Args, returns: Arg, exprs: BoolExpList) -> QCircui
qc.remove_identities()
return qc

def compile_expr(self, qc: QCircuit, expr: Boolean) -> int: # noqa: C901
def compile_expr(self, qc: QCircuit, expr: Boolean, dest=None) -> int: # noqa: C901
if isinstance(expr, Symbol):
return qc[expr.name]

Expand All @@ -105,31 +106,33 @@ def compile_expr(self, qc: QCircuit, expr: Boolean) -> int: # noqa: C901
self.expqmap.update_exp_for_qubit(eret, expr)
return eret
else:
fa = qc.get_free_ancilla()
qc.cx(eret, fa)
qc.x(fa)
if dest is None:
dest = qc.get_free_ancilla()
qc.cx(eret, dest)
qc.x(dest)
qc.mark_ancilla(eret)

self.garbage_collect(qc)
self.expqmap[expr] = fa
self.expqmap[expr] = dest

return fa
return dest

elif isinstance(expr, And):
erets = list(map(lambda e: self.compile_expr(qc, e), expr.args))
fa = qc.get_free_ancilla()
if dest is None:
dest = qc.get_free_ancilla()

qc.barrier("and")

qc.mcx(erets, fa)
qc.mcx(erets, dest)

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

self.garbage_collect(qc)

self.expqmap[expr] = fa
self.expqmap[expr] = dest

return fa
return dest

elif isinstance(expr, Xor):
erets = list(map(lambda e: self.compile_expr(qc, e), expr.args))
Expand All @@ -138,30 +141,32 @@ def compile_expr(self, qc: QCircuit, expr: Boolean) -> int: # noqa: C901
qc.barrier("xor")

if last in qc.ancilla_lst:
fa = last
dest = last
self.expqmap.update_exp_for_qubit(last, expr)
else:
fa = qc.get_free_ancilla()
if dest is None:
dest = qc.get_free_ancilla()

qc.cx(last, fa)
qc.cx(last, dest)
qc.mark_ancilla(last)
self.expqmap[expr] = fa
self.expqmap[expr] = dest

for x in erets:
qc.cx(x, fa)
qc.cx(x, dest)

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

return fa
return dest

elif isinstance(expr, BooleanFalse):
return qc.get_free_ancilla()

elif isinstance(expr, BooleanTrue):
fa = qc.get_free_ancilla()
qc.x(fa)
return fa
if dest is None:
dest = qc.get_free_ancilla()
qc.x(dest)
return dest

else:
raise CompilerException(expr)
10 changes: 5 additions & 5 deletions qlasskit/qlassf.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,23 @@ def remove_const_exps(exps: BoolExpList, fun_ret: Arg) -> BoolExpList:
# const: Dict[Symbol, Boolean] = {}
# n_exps: BoolExpList = []
# print(exps)

# for i in range(len(exps)):
# (s, e) = exps[i]
# e = e.subs(const)
# const[s] = e

# for x in e.free_symbols:
# if x in const:
# n_exps.append((x, const[x]))
# del const[x]

# for (s,e) in const.items():
# if s == e:
# continue

# n_exps.append((s,e))

# print(n_exps)
# print()
# print()
Expand Down
26 changes: 25 additions & 1 deletion qlasskit/types/qint.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from typing import List

from sympy import Symbol
from sympy.logic import And, Not, Or, false, true
from sympy.logic import And, Not, Or, Xor, false, true

from . import _eq, _full_adder, _neq
from .qtype import Qtype, TExp
Expand Down Expand Up @@ -171,6 +171,30 @@ def sub(cls, tleft: TExp, tright: TExp) -> TExp:
su = cls.add(an, cls.fill(tright)) # type: ignore
return cls.bitwise_not(su) # type: ignore

@classmethod
def bitwise_generic(cls, op, tleft: TExp, tright: TExp) -> TExp:
"""Bitwise generic"""
if len(tleft[1]) > len(tright[1]):
tright = tleft[0].fill(tright) # type: ignore

elif len(tleft[1]) < len(tright[1]):
tleft = tright[0].fill(tleft) # type: ignore

newl = [op(a, b) for (a, b) in zip(tleft[1], tright[1])]
return (tright[0], newl)

@classmethod
def bitwise_xor(cls, tleft: TExp, tright: TExp) -> TExp:
return cls.bitwise_generic(Xor, tleft, tright)

@classmethod
def bitwise_and(cls, tleft: TExp, tright: TExp) -> TExp:
return cls.bitwise_generic(And, tleft, tright)

@classmethod
def bitwise_or(cls, tleft: TExp, tright: TExp) -> TExp:
return cls.bitwise_generic(Or, tleft, tright)


class Qint2(Qint):
BIT_SIZE = 2
Expand Down
12 changes: 12 additions & 0 deletions qlasskit/types/qtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,15 @@ def add(tleft: TExp, tcomp: TExp) -> TExp:
@staticmethod
def sub(tleft: TExp, tcomp: TExp) -> TExp:
raise Exception("abstract")

@staticmethod
def bitwsie_xor(tleft: TExp, tcomp: TExp) -> TExp:
raise Exception("abstract")

@staticmethod
def bitwsie_and(tleft: TExp, tcomp: TExp) -> TExp:
raise Exception("abstract")

@staticmethod
def bitwsie_or(tleft: TExp, tcomp: TExp) -> TExp:
raise Exception("abstract")
17 changes: 17 additions & 0 deletions test/test_qlassf_bool.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,20 @@ def test_assign3(self):
# qf = qlassf(f, to_compile=False)
# self.assertEqual(len(qf.expressions), 2)
# self.assertEqual(qf.expressions[-1][1], ITE(d & e, g, h))


class TestQlassfBoolBitwise(unittest.TestCase):
def test_bitwise_and(self):
f = f"def test(a: bool, b: bool) -> bool:\n\treturn a & b"
qf = qlassf(f, to_compile=COMPILATION_ENABLED)
compute_and_compare_results(self, qf)

def test_bitwise_or(self):
f = f"def test(a: bool, b: bool) -> bool:\n\treturn a | b"
qf = qlassf(f, to_compile=COMPILATION_ENABLED)
compute_and_compare_results(self, qf)

def test_bitwise_xor(self):
f = f"def test(a: bool, b: bool) -> bool:\n\treturn a ^ b"
qf = qlassf(f, to_compile=COMPILATION_ENABLED)
compute_and_compare_results(self, qf)
24 changes: 24 additions & 0 deletions test/test_qlassf_int.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,30 @@ def test_sub_const3(self):
compute_and_compare_results(self, qf)


@parameterized_class(
("ttype", "ttype_str", "ttype_size"),
[
(Qint2, "Qint2", 2),
(Qint4, "Qint4", 4),
],
)
class TestQlassfIntBitwise(unittest.TestCase):
def test_bitwise_and(self):
f = f"def test(a: {self.ttype_str}, b: {self.ttype_str}) -> {self.ttype_str}:\n\treturn a & b"
qf = qlassf(f, to_compile=COMPILATION_ENABLED)
compute_and_compare_results(self, qf)

def test_bitwise_or(self):
f = f"def test(a: {self.ttype_str}, b: {self.ttype_str}) -> {self.ttype_str}:\n\treturn a | b"
qf = qlassf(f, to_compile=COMPILATION_ENABLED)
compute_and_compare_results(self, qf)

def test_bitwise_xor(self):
f = f"def test(a: {self.ttype_str}, b: {self.ttype_str}) -> {self.ttype_str}:\n\treturn a ^ b"
qf = qlassf(f, to_compile=COMPILATION_ENABLED)
compute_and_compare_results(self, qf)


class TestQlassfIntReassign(unittest.TestCase):
def test_reassign_newvar(self):
f = "def test(a: Qint2) -> Qint2:\n\tb = 0\n\tb = a + 1\n\treturn b"
Expand Down

0 comments on commit 7c32d47

Please sign in to comment.