From 05c3b9e9b297b3b3e0ec558a0bca7c315861f75f Mon Sep 17 00:00:00 2001 From: "Davide Gessa (dakk)" Date: Thu, 9 Nov 2023 10:51:39 +0100 Subject: [PATCH] remove deprecated optimizer --- qlasskit/boolopt/__init__.py | 1 + qlasskit/boolopt/bool_optimizer.py | 17 +-- qlasskit/boolopt/deprecated.py | 144 ------------------------- qlasskit/qlassf.py | 12 ++- test/test_bool_optimizer_deprecated.py | 73 ------------- 5 files changed, 11 insertions(+), 236 deletions(-) delete mode 100644 qlasskit/boolopt/deprecated.py delete mode 100644 test/test_bool_optimizer_deprecated.py diff --git a/qlasskit/boolopt/__init__.py b/qlasskit/boolopt/__init__.py index 9edd87e6..61dc1e66 100644 --- a/qlasskit/boolopt/__init__.py +++ b/qlasskit/boolopt/__init__.py @@ -14,3 +14,4 @@ # isort:skip_file from .sympytransformer import SympyTransformer # noqa: F401 +from .bool_optimizer import BoolOptimizerProfile, bestWorkingOptimizer # noqa: F401 diff --git a/qlasskit/boolopt/bool_optimizer.py b/qlasskit/boolopt/bool_optimizer.py index 77b158d7..1353e079 100644 --- a/qlasskit/boolopt/bool_optimizer.py +++ b/qlasskit/boolopt/bool_optimizer.py @@ -18,7 +18,7 @@ from sympy.logic.boolalg import And, Boolean, Not, Or, Xor, simplify_logic from ..ast2logic import BoolExpList -from . import SympyTransformer, deprecated +from . import SympyTransformer from .exp_transformers import ( remove_Implies, remove_ITE, @@ -83,18 +83,3 @@ def apply(self, exps): transform_or2and(), ] ) - - -deprecatedWorkingOptimizer = BoolOptimizerProfile( - [ - deprecated.remove_const_exps, - deprecated.remove_unnecessary_assigns, - deprecated.merge_unnecessary_assigns, - merge_expressions, - apply_cse, - remove_ITE(), - remove_Implies(), - transform_or2xor(), - transform_or2and(), - ] -) diff --git a/qlasskit/boolopt/deprecated.py b/qlasskit/boolopt/deprecated.py deleted file mode 100644 index 7983e409..00000000 --- a/qlasskit/boolopt/deprecated.py +++ /dev/null @@ -1,144 +0,0 @@ -# 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.boolalg import Boolean, simplify_logic, to_anf - -from ..ast2logic import BoolExpList - - -def remove_const_exps(exps: BoolExpList) -> BoolExpList: - """Remove const exps (replace a = True, b = ~a or c with b = c)""" - const: Dict[Symbol, Boolean] = {} - n_exps: BoolExpList = [] - for i in range(len(exps)): - (s, e) = exps[i] - e = e.subs(const) - if (e == False or e == True) and s.name[0:4] != "_ret": # noqa: E712 - const[s] = e - else: - if s in const: - del const[s] - n_exps.append((s, e)) - - return n_exps - - -def remove_unnecessary_assigns(exps: BoolExpList) -> BoolExpList: - """Remove exp like: __a.0 = a.0, ..., a.0 = __a.0""" - n_exps: BoolExpList = [] - - def should_add(s, e, n_exps2): - ename = f"__{s.name}" - if e.name == ename: - for s1, e1 in reversed(n_exps2): - if s1.name == ename: - if isinstance(e1, Symbol) and e1.name == s.name: - if all([s1 not in xe.free_symbols for (xs, xe) in n_exps]): - n_exps2.remove((s1, e1)) - return False - return True - else: - return True - return True - - for s, e in exps: - if not isinstance(e, Symbol) or should_add(s, e, n_exps): - n_exps.append((s, e)) - - return n_exps - - -def merge_unnecessary_assigns(exps: BoolExpList) -> BoolExpList: - """Translate exp like: __a.0 = !a, a = __a.0 ===> a = !a""" - n_exps: BoolExpList = [] - rep_d = {} - - for s, e in exps: - if len(n_exps) >= 1 and n_exps[-1][0] == e: # and n_exps[-1][0].name[2:] == s: - old = n_exps.pop() - rep_d[old[0]] = old[1] - n_exps.append((s, e.subs(rep_d))) - else: - n_exps.append((s, e.subs(rep_d))) - - return n_exps - - -def remove_unnecessary_aliases(exps: BoolExpList) -> BoolExpList: - """Translate exps like: (__d.0, a), (d.0, __d.0 & a) to => (d.0, a & a)""" - n_exps: BoolExpList = [] - rep_d = {} - - for s, e in exps: - if len(n_exps) >= 1 and n_exps[-1][0] in e.free_symbols: - old = n_exps.pop() - rep_d[old[0]] = old[1] - n_exps.append((s, e.subs(rep_d))) - else: - n_exps.append((s, e.subs(rep_d))) - - return n_exps - - -def remove_aliases(exps: BoolExpList) -> BoolExpList: - aliases = {} - n_exps = [] - for s, e in exps: - if isinstance(e, Symbol): - aliases[s] = e - elif s in aliases: - del aliases[s] - n_exps.append((s, e.subs(aliases))) - else: - n_exps.append((s, e.subs(aliases))) - - return n_exps - - -def s2_mega(exps: BoolExpList) -> BoolExpList: - n_exps: BoolExpList = [] - exp_d = {} - - for s, e in exps: - exp_d[s] = e - n_exps.append((s, e.subs(exp_d))) - - s_count = {} - exps = n_exps - - for s, e in exps: - if s.name not in s_count: - s_count[s.name] = 0 - - for x in e.free_symbols: - if x.name in s_count: - s_count[x.name] += 1 - - n_exps = [] - for s, e in exps: - if s_count[s.name] > 0 or s.name[0:4] == "_ret": - n_exps.append((s, e)) - - return n_exps - - -def exps_simplify(exps: BoolExpList) -> BoolExpList: - return list(map(lambda e: (e[0], simplify_logic(e[1])), exps)) - - -def exps_to_anf(exps: BoolExpList) -> BoolExpList: - return list(map(lambda e: (e[0], to_anf(e[1])), exps)) diff --git a/qlasskit/qlassf.py b/qlasskit/qlassf.py index 56ee53a5..13b9ba62 100644 --- a/qlasskit/qlassf.py +++ b/qlasskit/qlassf.py @@ -22,7 +22,7 @@ from .ast2ast import ast2ast from .ast2logic import Arg, Args, BoolExpList, LogicFun, flatten, translate_ast -from .boolopt.bool_optimizer import bestWorkingOptimizer +from .boolopt import BoolOptimizerProfile, bestWorkingOptimizer from .compiler import SupportedCompiler, to_quantum from .types import * # noqa: F403, F401 from .types import Qtype, type_repr @@ -180,6 +180,7 @@ def from_function( defs: List[LogicFun] = [], to_compile: bool = True, compiler: SupportedCompiler = "internal", + bool_optimizer: BoolOptimizerProfile = bestWorkingOptimizer, ) -> "QlassF": """Create a QlassF from a function or a string containing a function @@ -189,6 +190,8 @@ def from_function( defs (List[LogicFun]): list of LogicFun to inject to_compile (boolean, optional): if True, compile to quantum circuit (default: True) compiler (SupportedCompiler, optional): override default compiler (default: internal) + bool_optimizer (BoolOptimizerProfile, optional): override default optimizer + (default: bestWorkingOptimizer) """ if isinstance(f, str): exec(f) @@ -199,7 +202,7 @@ def from_function( fun_name, args, fun_ret, exps = translate_ast(fun, types, defs) original_f = eval(fun_name) if isinstance(f, str) else f - exps = bestWorkingOptimizer.apply(exps) + exps = bool_optimizer.apply(exps) # print(exps) @@ -217,6 +220,7 @@ def qlassf( defs: List[QlassF] = [], to_compile: bool = True, compiler: SupportedCompiler = "internal", + bool_optimizer: BoolOptimizerProfile = bestWorkingOptimizer, ) -> QlassF: """Decorator / function creating a QlassF object @@ -225,7 +229,9 @@ def qlassf( types (List[Qtype]): list of qtypes to inject defs (List[Qlassf]): list of qlassf to inject to_compile (boolean, optional): if True, compile to quantum circuit (default: True) - compiler (SupportedCompiler, optional): override default compiler (default: internal) + compiler (SupportedCompiler, optional): override default compiler (default: internal) + bool_optimizer (BoolOptimizerProfile, optional): override default optimizer + (default: bestWorkingOptimizer) """ defs_fun = list(map(lambda q: q.to_logicfun(), defs)) diff --git a/test/test_bool_optimizer_deprecated.py b/test/test_bool_optimizer_deprecated.py deleted file mode 100644 index e5cd9864..00000000 --- a/test/test_bool_optimizer_deprecated.py +++ /dev/null @@ -1,73 +0,0 @@ -# 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 parameterized import parameterized_class -from sympy import And, Not, Or, Symbol, symbols -from sympy.logic.boolalg import BooleanTrue - -from qlasskit.boolopt import deprecated - -a, b, c, d = symbols("a,b,c,d") -__a0 = Symbol("__a.0") -a0 = Symbol("a.0") -eTrue = And(True) -eFalse = And(False) - - -@parameterized_class( - ("exps", "n_exps"), - [ - ([], []), - ([(a, eTrue), (b, Or(Not(a), c))], [(b, c)]), - ([(a, eFalse), (b, And(Not(a), c))], [(b, c)]), - ([(a, eFalse), (b, Or(Not(a), c))], []), - ], -) -class TestBoolOptimizerDeprecated_remove_const_exps(unittest.TestCase): - def test_remove_const_exps(self): - n_exps = deprecated.remove_const_exps(self.exps) - self.assertEqual(self.n_exps, n_exps) - - -@parameterized_class( - ("exps", "n_exps"), - [ - ([], []), - ([(__a0, a0), (a0, __a0)], []), - ([(__a0, a0), (d, Not(b)), (a0, __a0)], [(d, Not(b))]), - ( - [(__a0, a0), (d, Not(__a0)), (a0, __a0)], - [(__a0, a0), (d, Not(__a0)), (a0, __a0)], - ), - ], -) -class TestBoolOptimizerDeprecated_remove_unnecessary_assigns(unittest.TestCase): - def test_remove_unnecessary_assigns(self): - n_exps = deprecated.remove_unnecessary_assigns(self.exps) - self.assertEqual(self.n_exps, n_exps) - - -@parameterized_class( - ("exps", "n_exps"), - [ - ([], []), - ([(__a0, Not(a)), (a, __a0)], [(a, Not(a))]), - ], -) -class TestBoolOptimizerDeprecated_merge_unnecessary_assigns(unittest.TestCase): - def test_merge_unnecessary_assigns(self): - n_exps = deprecated.merge_unnecessary_assigns(self.exps) - self.assertEqual(self.n_exps, n_exps)