From f1961e430f4dc75f5fcbd11c660a02338d22d2e5 Mon Sep 17 00:00:00 2001 From: "Davide Gessa (dakk)" Date: Fri, 13 Oct 2023 08:37:59 +0200 Subject: [PATCH] refactor project structure for types --- README.md | 8 +- TODO.md | 5 +- examples/ex2.py | 4 +- qlasskit/__init__.py | 11 +- qlasskit/ast2logic/__init__.py | 2 +- qlasskit/ast2logic/env.py | 3 +- qlasskit/ast2logic/t_arguments.py | 5 +- qlasskit/ast2logic/t_expression.py | 8 +- qlasskit/ast2logic/typing.py | 158 ----------------------------- qlasskit/qlassf.py | 5 +- qlasskit/types/__init__.py | 17 ++++ qlasskit/types/qbool.py | 25 +++++ qlasskit/types/qint.py | 158 +++++++++++++++++++++++++++++ qlasskit/types/qtype.py | 25 +++++ setup.py | 2 +- test/test_qlassf.py | 2 +- 16 files changed, 258 insertions(+), 180 deletions(-) create mode 100644 qlasskit/types/__init__.py create mode 100644 qlasskit/types/qbool.py create mode 100644 qlasskit/types/qint.py create mode 100644 qlasskit/types/qtype.py diff --git a/README.md b/README.md index 186893ab..b5e92e0d 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This tool will be useful for any algorithm that relies on a 'blackbox' function ```python @qlassf -def f(n: Int4) -> bool: +def f(n: Qint4) -> bool: if n == 3: return True else: @@ -30,7 +30,7 @@ qc.append(f.gate(), f.qubits_list(0)) Or, you can define a function with parameters: ```python @qlassf -def f(n: Int4, n_it: Param[int]) -> Int8: +def f(n: Qint4, n_it: Param[int]) -> Qint8: v = 0 for x in range(n_it): v += n @@ -49,13 +49,13 @@ Qlasskit (will) supports complex data types, like tuples and fixed size lists: ```python @qlassf -def f(a: Tuple[Int8, Int8]) -> Tuple[bool, bool]: +def f(a: Tuple[Qint8, Qint8]) -> Tuple[bool, bool]: return a[0] == 42, a[1] == 0 ``` ```python @qlassf -def search(alist: List4[Int2], to_search: Int2): +def search(alist: List4[Qint2], to_search: Qint2): for x in alist: if x == to_search: return True diff --git a/TODO.md b/TODO.md index 146d3d2e..9c3f583f 100644 --- a/TODO.md +++ b/TODO.md @@ -44,11 +44,11 @@ - [x] Doc: emphatize the compiler flow - [x] Doc: properly render documentation - [x] Builtin debug functions: print() +- [x] Fix code structure and typing location ### Week 4: (16 Oct 23) - [ ] Publish doc -- [ ] Fix structure and typing location -- [ ] Int arithmetic expressions (+, -, *, /) +- [ ] Int arithmetic: + - [ ] Function call - [ ] Builtin functions: sum(), max(), min(), len() @@ -56,6 +56,7 @@ ### Week 1: (23 Oct 23) - [ ] Inner function +- [ ] Int arithmetic expressions (-, *, /) - [ ] Parametrized qlassf ### Week 2: (30 Oct 23) diff --git a/examples/ex2.py b/examples/ex2.py index 3414dfa8..e1a0103e 100644 --- a/examples/ex2.py +++ b/examples/ex2.py @@ -1,10 +1,10 @@ from qiskit import QuantumCircuit -from qlasskit import Int8, Param, qlassf +from qlasskit import Param, Qint8, qlassf @qlassf -def f(n: Int4, n_it: Param[int]) -> Int8: +def f(n: Qint4, n_it: Param[int]) -> Qint8: v = 0 for x in range(n_it): v += n diff --git a/qlasskit/__init__.py b/qlasskit/__init__.py index bb19f8be..1a2208c3 100644 --- a/qlasskit/__init__.py +++ b/qlasskit/__init__.py @@ -18,4 +18,13 @@ from .qcircuit import QCircuit # noqa: F401 from .qlassf import QlassF, qlassf # noqa: F401 from .ast2logic import exceptions # noqa: F401 -from .ast2logic.typing import * # noqa: F401, F403 +from .types import ( # noqa: F401, F403 + Qtype, + Qbool, + Qint, + Qint2, + Qint4, + Qint8, + Qint12, + Qint16, +) diff --git a/qlasskit/ast2logic/__init__.py b/qlasskit/ast2logic/__init__.py index f83ec2d5..97395573 100644 --- a/qlasskit/ast2logic/__init__.py +++ b/qlasskit/ast2logic/__init__.py @@ -15,9 +15,9 @@ from .env import Env, Binding # noqa: F401, E402 from .utils import flatten # noqa: F401, E402 +from .typing import Args, BoolExpList # noqa: F401, E402 from .t_arguments import translate_argument, translate_arguments # noqa: F401, E402 from .t_expression import translate_expression, type_of_exp # noqa: F401, E402 from .t_statement import translate_statement # noqa: F401, E402 from .t_ast import translate_ast # noqa: F401, E402 -from .typing import Qint, Qint2, Qint4, Qint8, Qint12, Qint16, Qtype # noqa: F401 from . import exceptions # noqa: F401, E402 diff --git a/qlasskit/ast2logic/env.py b/qlasskit/ast2logic/env.py index 0c78b016..b60dbdfe 100644 --- a/qlasskit/ast2logic/env.py +++ b/qlasskit/ast2logic/env.py @@ -16,8 +16,9 @@ from typing_extensions import TypeAlias +from ..types import Qint # noqa: F401, E402 from . import exceptions -from .typing import Arg, Qint # noqa: F401, E402 +from .typing import Arg Binding: TypeAlias = Arg diff --git a/qlasskit/ast2logic/t_arguments.py b/qlasskit/ast2logic/t_arguments.py index 37df0bf4..293457e4 100644 --- a/qlasskit/ast2logic/t_arguments.py +++ b/qlasskit/ast2logic/t_arguments.py @@ -14,9 +14,10 @@ import ast from typing import List, Tuple +from ..types import * # noqa: F401, F403 +from ..types import TType from . import exceptions -from .typing import * # noqa: F401, F403 -from .typing import Arg, Args, TType +from .typing import Arg, Args def translate_argument(ann, base="") -> Arg: diff --git a/qlasskit/ast2logic/t_expression.py b/qlasskit/ast2logic/t_expression.py index ad729905..ea7de0cc 100644 --- a/qlasskit/ast2logic/t_expression.py +++ b/qlasskit/ast2logic/t_expression.py @@ -17,8 +17,8 @@ from sympy import Symbol from sympy.logic import ITE, And, Not, Or, false, true +from ..types import Qbool, Qint, Qint2, Qint4, Qint8, Qint12, Qint16, TExp from . import Env, exceptions -from .typing import Qint, Qint2, Qint4, Qint8, Qint12, Qint16, TExp, bool_eq, bool_neq def type_of_exp(vlist, base, res=[]) -> List[Symbol]: @@ -173,7 +173,7 @@ def unfold(v_exps, op): # Eq if isinstance(expr.ops[0], ast.Eq): if tleft[0] == bool and tcomp[0] == bool: - return (bool, bool_eq(tleft[1], tcomp[1])) + return (bool, Qbool.eq(tleft[1], tcomp[1])) elif issubclass(tleft[0], Qint) and issubclass(tcomp[0], Qint): # type: ignore return Qint.eq(tleft, tcomp) @@ -182,9 +182,9 @@ def unfold(v_exps, op): # NotEq elif isinstance(expr.ops[0], ast.NotEq): if tleft[0] == bool and tcomp[0] == bool: - return (bool, bool_neq(tleft[1], tcomp[1])) + return (bool, Qbool.neq(tleft[1], tcomp[1])) elif issubclass(tleft[0], Qint) and issubclass(tcomp[0], Qint): # type: ignore - return Qint.not_eq(tleft, tcomp) + return Qint.neq(tleft, tcomp) raise exceptions.TypeErrorException(tcomp[0], tleft[0]) diff --git a/qlasskit/ast2logic/typing.py b/qlasskit/ast2logic/typing.py index 08b67d18..e52bb584 100644 --- a/qlasskit/ast2logic/typing.py +++ b/qlasskit/ast2logic/typing.py @@ -15,24 +15,7 @@ from typing import List, Tuple from sympy import Symbol -from sympy.logic import And, Not, Or, Xor, false, true from sympy.logic.boolalg import Boolean -from typing_extensions import TypeAlias - -# from .ast2logic.t_expression import TType - -TType: TypeAlias = object -TExp: TypeAlias = Tuple[TType, Boolean] - - -# XOR -def bool_neq(a, b): - return Xor(a, b) - - -# !XOR -def bool_eq(a, b): - return Not(bool_neq(a, b)) class Arg: @@ -56,144 +39,3 @@ def to_exp(self) -> List[Symbol]: Args = List[Arg] BoolExpList = List[Tuple[Symbol, Boolean]] LogicFun = Tuple[str, Args, int, BoolExpList] - - -class Qtype: - BIT_SIZE = 0 - - -class Qint(int, Qtype): - BIT_SIZE = 8 - - def __init__(self, value): - super().__init__() - self.value = value - - def __getitem__(self, i): - if i > self.BIT_SIZE: - raise Exception("Unbound") - - return self.to_bool_str()[i] == "1" - - @classmethod - def from_bool(cls, v: List[bool]): - return cls(int("".join(map(lambda x: "1" if x else "0", v[::-1])), 2)) - - def to_bool_str(self) -> str: - s = bin(self.value)[2:][0 : self.BIT_SIZE] - return ("0" * (self.BIT_SIZE - len(s)) + s)[::-1] - - @staticmethod - def const(v: int) -> List[bool]: - """Return the list of bool representing an int""" - return list(map(lambda c: True if c == "1" else False, bin(v)[2:]))[::-1] - - @staticmethod - def fill(v: Tuple[TType, List[bool]]) -> Tuple[TType, List[bool]]: - """Fill a Qint to reach its bit_size""" - if len(v[1]) < v[0].BIT_SIZE: # type: ignore - v = ( - v[0], - (v[0].BIT_SIZE - len(v[1])) * v[1] + [False], # type: ignore - ) - return v - - @staticmethod - def eq(tleft: TExp, tcomp: TExp) -> TExp: - """Compare two Qint for equality""" - ex = true - for x in zip(tleft[1], tcomp[1]): - ex = And(ex, bool_eq(x[0], x[1])) - - if len(tleft[1]) > len(tcomp[1]): - for x in tleft[1][len(tcomp[1]) :]: - ex = And(ex, Not(x)) - - if len(tleft[1]) < len(tcomp[1]): - for x in tcomp[1][len(tleft[1]) :]: - ex = And(ex, Not(x)) - - return (bool, ex) - - @staticmethod - def not_eq(tleft: TExp, tcomp: TExp) -> TExp: - """Compare two Qint for inequality""" - ex = false - for x in zip(tleft[1], tcomp[1]): - ex = Or(ex, bool_neq(x[0], x[1])) - - if len(tleft[1]) > len(tcomp[1]): - for x in tleft[1][len(tcomp[1]) :]: - ex = Or(ex, x) - - if len(tleft[1]) < len(tcomp[1]): - for x in tcomp[1][len(tleft[1]) :]: - ex = Or(ex, x) - - return (bool, ex) - - @staticmethod - def gt(tleft: TExp, tcomp: TExp) -> TExp: - """Compare two Qint for greater than""" - prev: List[Symbol] = [] - - for a, b in list(zip(tleft[1], tcomp[1]))[::-1]: - if len(prev) == 0: - ex = And(a, Not(b)) - else: - ex = Or(ex, And(*(prev + [a, Not(b)]))) - - prev.append(bool_eq(a, b)) - - if len(tleft[1]) > len(tcomp[1]): - for x in tleft[1][len(tcomp[1]) :]: - ex = Or(ex, x) - - if len(tleft[1]) < len(tcomp[1]): - for x in tcomp[1][len(tleft[1]) :]: - ex = Or(ex, x) - - return (bool, ex) - - @staticmethod - def lt(tleft: TExp, tcomp: TExp) -> TExp: - """Compare two Qint for lower than""" - return (bool, And(Not(Qint.gt(tleft, tcomp)[1]), Not(Qint.eq(tleft, tcomp)[1]))) - - @staticmethod - def lte(tleft: TExp, tcomp: TExp) -> TExp: - """Compare two Qint for lower than - equal""" - return (bool, Not(Qint.gt(tleft, tcomp)[1])) - - @staticmethod - def gte(tleft: TExp, tcomp: TExp) -> TExp: - """Compare two Qint for greater than - equal""" - return (bool, Not(Qint.lt(tleft, tcomp)[1])) - - # @staticmethod - # def add(tleft: TExp, tright: TExp) -> TExp: - # """Add two Qint""" - - -class Qint2(Qint): - BIT_SIZE = 2 - - -class Qint4(Qint): - BIT_SIZE = 4 - - -class Qint8(Qint): - BIT_SIZE = 8 - - -class Qint12(Qint): - BIT_SIZE = 12 - - -class Qint16(Qint): - BIT_SIZE = 16 - - -# class Qlist -# class Qfixed diff --git a/qlasskit/qlassf.py b/qlasskit/qlassf.py index c4c35baf..fd5c5ed9 100644 --- a/qlasskit/qlassf.py +++ b/qlasskit/qlassf.py @@ -18,9 +18,8 @@ from typing import Callable, List, Tuple, Union # noqa: F401 from . import compiler -from .ast2logic import flatten, translate_ast -from .ast2logic.typing import * # noqa: F403, F401 -from .ast2logic.typing import Args, BoolExpList +from .ast2logic import Args, BoolExpList, flatten, translate_ast +from .types import * # noqa: F403, F401 MAX_TRUTH_TABLE_SIZE = 20 diff --git a/qlasskit/types/__init__.py b/qlasskit/types/__init__.py new file mode 100644 index 00000000..649eb3b1 --- /dev/null +++ b/qlasskit/types/__init__.py @@ -0,0 +1,17 @@ +# 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 .qbool import Qbool # noqa: F401 +from .qint import Qint, Qint2, Qint4, Qint8, Qint12, Qint16 # noqa: F401 +from .qtype import Qtype, TExp, TType # noqa: F401 diff --git a/qlasskit/types/qbool.py b/qlasskit/types/qbool.py new file mode 100644 index 00000000..e52b72bb --- /dev/null +++ b/qlasskit/types/qbool.py @@ -0,0 +1,25 @@ +# 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 sympy.logic import Not, Xor + + +class Qbool: + @staticmethod + def neq(a, b): + return Xor(a, b) + + @staticmethod + def eq(a, b): + return Not(Qbool.neq(a, b)) diff --git a/qlasskit/types/qint.py b/qlasskit/types/qint.py new file mode 100644 index 00000000..cf461fe3 --- /dev/null +++ b/qlasskit/types/qint.py @@ -0,0 +1,158 @@ +# 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 List, Tuple + +from sympy import Symbol +from sympy.logic import And, Not, Or, false, true + +from .qbool import Qbool +from .qtype import Qtype, TExp, TType + + +class Qint(int, Qtype): + BIT_SIZE = 8 + + def __init__(self, value): + super().__init__() + self.value = value + + def __getitem__(self, i): + if i > self.BIT_SIZE: + raise Exception("Unbound") + + return self.to_bool_str()[i] == "1" + + @classmethod + def from_bool(cls, v: List[bool]): + return cls(int("".join(map(lambda x: "1" if x else "0", v[::-1])), 2)) + + def to_bool_str(self) -> str: + s = bin(self.value)[2:][0 : self.BIT_SIZE] + return ("0" * (self.BIT_SIZE - len(s)) + s)[::-1] + + @staticmethod + def const(v: int) -> List[bool]: + """Return the list of bool representing an int""" + return list(map(lambda c: True if c == "1" else False, bin(v)[2:]))[::-1] + + @staticmethod + def fill(v: Tuple[TType, List[bool]]) -> Tuple[TType, List[bool]]: + """Fill a Qint to reach its bit_size""" + if len(v[1]) < v[0].BIT_SIZE: # type: ignore + v = ( + v[0], + (v[0].BIT_SIZE - len(v[1])) * v[1] + [False], # type: ignore + ) + return v + + @staticmethod + def eq(tleft: TExp, tcomp: TExp) -> TExp: + """Compare two Qint for equality""" + ex = true + for x in zip(tleft[1], tcomp[1]): + ex = And(ex, Qbool.eq(x[0], x[1])) + + if len(tleft[1]) > len(tcomp[1]): + for x in tleft[1][len(tcomp[1]) :]: + ex = And(ex, Not(x)) + + if len(tleft[1]) < len(tcomp[1]): + for x in tcomp[1][len(tleft[1]) :]: + ex = And(ex, Not(x)) + + return (bool, ex) + + @staticmethod + def neq(tleft: TExp, tcomp: TExp) -> TExp: + """Compare two Qint for inequality""" + ex = false + for x in zip(tleft[1], tcomp[1]): + ex = Or(ex, Qbool.neq(x[0], x[1])) + + if len(tleft[1]) > len(tcomp[1]): + for x in tleft[1][len(tcomp[1]) :]: + ex = Or(ex, x) + + if len(tleft[1]) < len(tcomp[1]): + for x in tcomp[1][len(tleft[1]) :]: + ex = Or(ex, x) + + return (bool, ex) + + @staticmethod + def gt(tleft: TExp, tcomp: TExp) -> TExp: + """Compare two Qint for greater than""" + prev: List[Symbol] = [] + + for a, b in list(zip(tleft[1], tcomp[1]))[::-1]: + if len(prev) == 0: + ex = And(a, Not(b)) + else: + ex = Or(ex, And(*(prev + [a, Not(b)]))) + + prev.append(Qbool.eq(a, b)) + + if len(tleft[1]) > len(tcomp[1]): + for x in tleft[1][len(tcomp[1]) :]: + ex = Or(ex, x) + + if len(tleft[1]) < len(tcomp[1]): + for x in tcomp[1][len(tleft[1]) :]: + ex = Or(ex, x) + + return (bool, ex) + + @staticmethod + def lt(tleft: TExp, tcomp: TExp) -> TExp: + """Compare two Qint for lower than""" + return (bool, And(Not(Qint.gt(tleft, tcomp)[1]), Not(Qint.eq(tleft, tcomp)[1]))) + + @staticmethod + def lte(tleft: TExp, tcomp: TExp) -> TExp: + """Compare two Qint for lower than - equal""" + return (bool, Not(Qint.gt(tleft, tcomp)[1])) + + @staticmethod + def gte(tleft: TExp, tcomp: TExp) -> TExp: + """Compare two Qint for greater than - equal""" + return (bool, Not(Qint.lt(tleft, tcomp)[1])) + + # @staticmethod + # def add(tleft: TExp, tright: TExp) -> TExp: + # """Add two Qint""" + + +class Qint2(Qint): + BIT_SIZE = 2 + + +class Qint4(Qint): + BIT_SIZE = 4 + + +class Qint8(Qint): + BIT_SIZE = 8 + + +class Qint12(Qint): + BIT_SIZE = 12 + + +class Qint16(Qint): + BIT_SIZE = 16 + + +# class Qlist +# class Qfixed diff --git a/qlasskit/types/qtype.py b/qlasskit/types/qtype.py new file mode 100644 index 00000000..34999621 --- /dev/null +++ b/qlasskit/types/qtype.py @@ -0,0 +1,25 @@ +# 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 + +from sympy.logic.boolalg import Boolean +from typing_extensions import TypeAlias + +TType: TypeAlias = object +TExp: TypeAlias = Tuple[TType, Boolean] + + +class Qtype: + BIT_SIZE = 0 diff --git a/setup.py b/setup.py index e505b7d6..a4a47953 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ author="Davide Gessa", setup_requires="setuptools", author_email="gessadavide@gmail.com", - packages=["qlasskit", "qlasskit.ast2logic", "qlasskit.compiler"], + packages=["qlasskit", "qlasskit.types", "qlasskit.ast2logic", "qlasskit.compiler"], zip_safe=False, install_requires=[ "sympy==1.12", diff --git a/test/test_qlassf.py b/test/test_qlassf.py index f62db8d5..6fab4b91 100644 --- a/test/test_qlassf.py +++ b/test/test_qlassf.py @@ -14,7 +14,7 @@ import unittest -from qlasskit import Qint4, QlassF, qlassf +from qlasskit import Qint4, Qint12, QlassF, qlassf from . import utils from .utils import COMPILATION_ENABLED, compute_and_compare_results