Skip to content

Commit

Permalink
qmatrix type
Browse files Browse the repository at this point in the history
  • Loading branch information
dakk committed Nov 27, 2023
1 parent e52a349 commit d60c4ba
Show file tree
Hide file tree
Showing 12 changed files with 255 additions and 11 deletions.
1 change: 1 addition & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@

### Week 2: (27 Nov 23)

- [x] Qmatrix
- [ ] Move all examples to doc
- [ ] Improve documentation
- [ ] First stable release
Expand Down
6 changes: 5 additions & 1 deletion docs/source/algorithms.rst
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
Algorithms
====================================
====================================


Grover search
-----------------
10 changes: 5 additions & 5 deletions docs/source/example_grover_2.ipynb

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions docs/source/supported.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ Qlist[T, size] denotes a fixed-size list in qlasskit.
For example, the list `[1,2,3]` is typed as `Qlist[Qint2,3]`.


Matrix
^^^^

Qmatrix[T, m, n] denotes a fixed-size list in qlasskit.
For example, the matrix `[[1,2],[3,4]]` is typed as `Qmatrix[Qint2,2,2]`.





Expressions
-----------
Expand Down Expand Up @@ -77,6 +86,14 @@ List (fixed size)
[a, b]
2D Matrix (fixed size)
^^^^^^^^^^^^^^^^^

.. code-block:: python
[[a, b], [c,d]]
Subscript
^^^^^^^^^

Expand Down
20 changes: 20 additions & 0 deletions examples/ex_matrix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from matplotlib import pyplot as plt
from qiskit import Aer, QuantumCircuit, transpile
from qiskit.visualization import plot_histogram

from qlasskit import Qint2, Qint4, Qint8, Qmatrix, qlassf
from qlasskit.algorithms import Grover


@qlassf
def my_fun(a_mat: Qmatrix[bool, 3, 3]) -> bool:
return a_mat[0][1] and a_mat[1][1]


print(my_fun.expressions)

my_fun.circuit().export().draw(
"mpl",
style={"textcolor": "#ffab40", "backgroundcolor": "black", "linecolor": "#444"},
)
plt.show()
1 change: 1 addition & 0 deletions qlasskit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@
Qint12,
Qint16,
Qlist,
Qmatrix,
)
from .boolquant import Q # noqa: F401
39 changes: 37 additions & 2 deletions qlasskit/ast2ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def _replace_types_annotations(ann, arg=None):
slice=_ituple,
)

# Replace Qlist[T,n] with Tuple[(T,)*3]
# Replace Qlist[T,n] with Tuple[(T,)*n]
if isinstance(ann, ast.Subscript) and ann.value.id == "Qlist":
_elts = ann.slice.elts
_ituple = ast.Tuple(elts=[copy.deepcopy(_elts[0])] * _elts[1].value)
Expand All @@ -61,6 +61,17 @@ def _replace_types_annotations(ann, arg=None):
slice=_ituple,
)

# Replace Qmatrix[T,n,m] with Tuple[(Tuple[(T,)*m],)*n]
if isinstance(ann, ast.Subscript) and ann.value.id == "Qmatrix":
_elts = ann.slice.elts
_ituple_row = ast.Tuple(elts=[copy.deepcopy(_elts[0])] * _elts[2].value)
_ituple = ast.Tuple(elts=[copy.deepcopy(_ituple_row)] * _elts[1].value)

ann = ast.Subscript(
value=ast.Name(id="Tuple", ctx=ast.Load()),
slice=_ituple,
)

if arg is not None:
arg.annotation = ann
return arg
Expand Down Expand Up @@ -323,7 +334,7 @@ def visit_AugAssign(self, node):
),
]

def visit_For(self, node):
def visit_For(self, node): # noqa: C901
iter = self.visit(node.iter)

# Iterate over an object
Expand All @@ -344,6 +355,30 @@ def visit_For(self, node):
]
elif isinstance(iter, ast.Tuple):
iter = iter.elts
elif isinstance(iter, ast.Subscript) and iter.value.id in self.env:
if isinstance(self.env[iter.value.id], ast.Tuple):
iter = self.env[iter.value.id].elts[iter.slice.value]

elif isinstance(self.env[iter.value.id], ast.Subscript):
_elts = self.env[iter.value.id].slice.elts[iter.slice.value]

if isinstance(_elts, ast.Tuple):
_elts = _elts.elts

iter = [
ast.Subscript(
value=ast.Subscript(
value=ast.Name(id=iter.value.id, ctx=ast.Load()),
slice=ast.Constant(value=iter.slice.value),
ctx=ast.Load(),
),
slice=ast.Constant(value=e),
)
for e in range(len(_elts))
]

if isinstance(iter, ast.Constant) and isinstance(iter.value, ast.Tuple):
iter = iter.value.elts

rolls = []
for i in iter:
Expand Down
23 changes: 21 additions & 2 deletions qlasskit/ast2logic/t_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@
from .typing import Arg, Args


def translate_argument(ann, env, base="") -> Arg:
def translate_argument(ann, env, base="") -> Arg: # noqa: C901
def to_name(a):
return a.attr if isinstance(a, ast.Attribute) else a.id

ttypes: List[TType] = []

# Tuple
if isinstance(ann, ast.Subscript) and ann.value.id == "Tuple": # type: ignore
al = []
ind = 0
ttypes: List[TType] = []

if hasattr(ann.slice, "elts"):
_elts = ann.slice.elts # type: ignore
Expand All @@ -47,6 +48,24 @@ def to_name(a):
ttypes_t = tuple(ttypes)
return Arg(base, Tuple[ttypes_t], al)

elif isinstance(ann, ast.Tuple):
al = []
ind = 0

_elts = ann.elts # type: ignore

for i in _elts: # type: ignore
if isinstance(i, ast.Name) and to_name(i) == "bool":
al.append(f"{base}.{ind}")
ttypes.append(bool)
else:
inner_arg = translate_argument(i, env, base=f"{base}.{ind}")
ttypes.append(inner_arg.ttype)
al.extend(inner_arg.bitvec)
ind += 1
ttypes_t = tuple(ttypes)
return Arg(base, Tuple[ttypes_t], al)

# Bool
elif to_name(ann) == "bool":
return Arg(base, bool, [f"{base}"])
Expand Down
8 changes: 8 additions & 0 deletions qlasskit/ast2logic/t_expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ def unfold(v_exps, op):
return (bool, true)
elif expr.value is False:
return (bool, false)
elif isinstance(expr.value, ast.Tuple):
types = []
values = []
for x in expr.value.elts: # type: ignore
t, e = const_to_qtype(x.value) # type: ignore
types.append(t)
values.append(e)
return types, values

q_value = const_to_qtype(expr.value)

Expand Down
3 changes: 2 additions & 1 deletion qlasskit/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ def _full_adder(c, a, b): # Carry x Sum
from .qtype import Qtype, TExp, TType # noqa: F401, E402
from .qbool import Qbool # noqa: F401, E402
from .qlist import Qlist # noqa: F401, E402
from .qmatrix import Qmatrix # noqa: F401, E402
from .qint import Qint, Qint2, Qint3, Qint4, Qint8, Qint12, Qint16 # noqa: F401, E402

BUILTIN_TYPES = [Qint2, Qint3, Qint4, Qint8, Qint12, Qint16, Qlist]
BUILTIN_TYPES = [Qint2, Qint3, Qint4, Qint8, Qint12, Qint16, Qlist, Qmatrix]


def const_to_qtype(value: Any) -> TExp:
Expand Down
38 changes: 38 additions & 0 deletions qlasskit/types/qmatrix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# 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 Tuple, TypeVar

T = TypeVar("T")


class QmatrixMeta(type):
def __getitem__(cls, params):
if isinstance(params, tuple) and len(params) == 3:
T, n, m = params
if (
isinstance(T, type)
and isinstance(n, int)
and n > 0
and isinstance(m, int)
and m > 0
):
return Tuple[(Tuple[(T,) * n],) * m]


class Qmatrix(metaclass=QmatrixMeta):
pass


# a: Qmatrix[int, 2, 2] => Tuple[Tuple[int, int], Tuple[int, int]]
100 changes: 100 additions & 0 deletions test/test_qlassf_matrix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# 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.

import unittest
from typing import Tuple

from parameterized import parameterized_class
from sympy import Symbol, symbols
from sympy.logic import ITE, And, Not, Or, false, simplify_logic, true

from qlasskit import QlassF, exceptions, qlassf

from .utils import COMPILATION_ENABLED, ENABLED_COMPILERS, compute_and_compare_results

a, b, c, d = symbols("a,b,c,d")
_ret = Symbol("_ret")
a_0 = Symbol("a.0")
a_1 = Symbol("a.1")
b_0 = Symbol("b.0")
b_1 = Symbol("b.1")


@parameterized_class(("compiler"), ENABLED_COMPILERS)
class TestQlassfMatrix(unittest.TestCase):
def test_matrix_const_2x2(self):
f = "def test() -> Qmatrix[bool, 2, 2]:\n\treturn [[True, True], [True, False]]"
qf = qlassf(f, to_compile=COMPILATION_ENABLED, compiler=self.compiler)
compute_and_compare_results(self, qf)

def test_matrix_const_2x3(self):
f = "def test() -> Qmatrix[bool, 2, 3]:\n\treturn [[True, True, True], [True, False, True]]"
qf = qlassf(f, to_compile=COMPILATION_ENABLED, compiler=self.compiler)
compute_and_compare_results(self, qf)

def test_matrix_const_3x2(self):
f = "def test() -> Qmatrix[bool, 3, 2]:\n\treturn [[True, True], [True, False], [True, False]]"
qf = qlassf(f, to_compile=COMPILATION_ENABLED, compiler=self.compiler)
compute_and_compare_results(self, qf)

def test_matrix_access(self):
f = "def test(a: Qmatrix[bool, 2, 2]) -> bool:\n\treturn a[0][1]"
qf = qlassf(f, to_compile=COMPILATION_ENABLED, compiler=self.compiler)
self.assertEqual(len(qf.expressions), 1)
self.assertEqual(qf.expressions[0][0], _ret)
self.assertEqual(qf.expressions[0][1], Symbol("a.0.1"))
compute_and_compare_results(self, qf)

def test_matrix_iterator_var(self):
f = "def test(a: Qmatrix[Qint2, 2, 2]) -> Qint2:\n\tc = 0\n\tfor x in a:\n\t\tfor y in x:\n\t\t\tc += y\n\treturn c"
qf = qlassf(f, to_compile=COMPILATION_ENABLED, compiler=self.compiler)
compute_and_compare_results(self, qf)

def test_matrix_iterator_list(self):
f = "def test(a: Qint2) -> Qint2:\n\tc = 0\n\tfor x in [[1,2],[3,4]]:\n\t\tfor y in x:\n\t\t\tc += y + a\n\treturn c"
qf = qlassf(f, to_compile=COMPILATION_ENABLED, compiler=self.compiler)
compute_and_compare_results(self, qf)

def test_matrix_iterator_varlist(self):
f = "def test(a: Qint2) -> Qint2:\n\tc = [[1,2],[3,4]]\n\tfor x in c:\n\t\tfor y in x:\n\t\t\ta += y\n\treturn a"
qf = qlassf(f, to_compile=COMPILATION_ENABLED, compiler=self.compiler)
compute_and_compare_results(self, qf)

def test_matrix_len(self):
f = "def test(a: Qmatrix[Qint2, 2, 2]) -> Qint2:\n\tc = 0\n\tfor x in range(len(a)):\n\t\tc += a[x][0]\n\treturn c"
qf = qlassf(f, to_compile=COMPILATION_ENABLED, compiler=self.compiler)
compute_and_compare_results(self, qf)

# def test_matrix_access_with_var(self):
# f = "def test(a: Qint2) -> Qint2:\n\tc = [[1,2],[3,4]]\n\tb = c[a][a]\n\treturn b"
# qf = qlassf(f, to_compile=COMPILATION_ENABLED, compiler=self.compiler)
# compute_and_compare_results(self, qf)

# def test_list_access_with_var_on_tuple(self):
# # TODO: this fails on internal compiler
# if self.compiler == "internal":
# return

# f = "def test(ab: Tuple[Qint2, Qint2]) -> Qint2:\n\tc = [1,2,3,2]\n\tai,bi = ab\n\td = c[ai] + c[bi]\n\treturn d"
# qf = qlassf(f, to_compile=COMPILATION_ENABLED, compiler=self.compiler)
# compute_and_compare_results(self, qf)

# def test_list_access_with_var_on_tuple2(self):
# # TODO: this fails on internal compiler
# if self.compiler == "internal":
# return

# f = "def test(ab: Tuple[Qint2, Qint2]) -> Qint2:\n\tc = [1,2,3,2]\n\td = c[ab[0]] + c[ab[1]]\n\treturn d"
# qf = qlassf(f, to_compile=COMPILATION_ENABLED, compiler=self.compiler)
# compute_and_compare_results(self, qf)

0 comments on commit d60c4ba

Please sign in to comment.