From 6ffdecb15e68e09460013573b6bb968201998c9e Mon Sep 17 00:00:00 2001 From: "Davide Gessa (dakk)" Date: Wed, 6 Dec 2023 15:27:48 +0100 Subject: [PATCH] fast optimizer and big circuit example --- TODO.md | 9 ++-- docs/source/example_big_circuit.ipynb | 74 +++++++++++++++++++++++++++ docs/source/index.rst | 1 + qlasskit/boolopt/__init__.py | 6 ++- qlasskit/boolopt/bool_optimizer.py | 14 ++++- qlasskit/qcircuit/qcircuit.py | 4 ++ qlasskit/qlassfun.py | 12 ++--- qlasskit/types/qint.py | 4 +- test/test_qlassf.py | 4 +- 9 files changed, 112 insertions(+), 16 deletions(-) create mode 100644 docs/source/example_big_circuit.ipynb diff --git a/TODO.md b/TODO.md index 9575d777..9706022a 100644 --- a/TODO.md +++ b/TODO.md @@ -118,6 +118,7 @@ - [x] Int arithmetic: mod - [x] Simon example - [x] Deutsch-Jozsa example +- [x] Improve performance on big circuits ### Week 4: (11 Dec 23) @@ -134,23 +135,23 @@ ## Future features -- [ ] Int arithmetic: div -- [ ] Lambda -- [ ] Builtin function: map ### Language support +- [ ] Int arithmetic: div +- [ ] Lambda +- [ ] Builtin function: map - [ ] Datatype: Char - [ ] Datatype: Dict - [ ] Datatype: Float - [ ] Datatype: Enum - [ ] While loop - [ ] Recursion -- [ ] Parameter bind - [ ] Float arithmetic ### Abstraction support +- [ ] Parameter bind - [ ] Extract boolean expressions from quantum circuits ### Framwork support diff --git a/docs/source/example_big_circuit.ipynb b/docs/source/example_big_circuit.ipynb new file mode 100644 index 00000000..1eec2d61 --- /dev/null +++ b/docs/source/example_big_circuit.ipynb @@ -0,0 +1,74 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Example: big circuits" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Qlasskit is capable of producing large circuit without any issue. The only thing that you have to do, is to use the `fastOptimizer`, since running CSE is too slow on large expressions lists.\n", + "\n", + "In the next example we are going to create a quantum circuit with 64 `Qint8` in input, and one `Qint8` in output, resulting on a circuit of ~1000 qubits and ~16000 gates in around 2 seconds." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "from qlasskit import Qint8, Qlist, boolopt, qlassfa\n", + "\n", + "@qlassfa(bool_optimizer=boolopt.fastOptimizer)\n", + "def test(message: Qlist[Qint8, 64]) -> Qint8:\n", + " h_val = Qint8(0)\n", + " for c in message:\n", + " h_val = ((h_val >> 4) + c) & 0xFF\n", + " return h_val" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "QCircuit test\t\tGates: 16540\t\tQubits: 1039\n" + ] + } + ], + "source": [ + "print(test.circuit())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "qlasskit_310-env", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/source/index.rst b/docs/source/index.rst index 3af894d9..5510e729 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -25,6 +25,7 @@ Python and translate them into unitary operators (gates) for use in quantum circ example_simon.ipynb example_deutsch_jozsa.ipynb example_unitary_of_f.ipynb + example_big_circuit.ipynb Indices and tables diff --git a/qlasskit/boolopt/__init__.py b/qlasskit/boolopt/__init__.py index 61dc1e66..21f26001 100644 --- a/qlasskit/boolopt/__init__.py +++ b/qlasskit/boolopt/__init__.py @@ -14,4 +14,8 @@ # isort:skip_file from .sympytransformer import SympyTransformer # noqa: F401 -from .bool_optimizer import BoolOptimizerProfile, bestWorkingOptimizer # noqa: F401 +from .bool_optimizer import ( # noqa: F401 + BoolOptimizerProfile, + defaultOptimizer, + fastOptimizer, +) diff --git a/qlasskit/boolopt/bool_optimizer.py b/qlasskit/boolopt/bool_optimizer.py index 516b4ebf..905837be 100644 --- a/qlasskit/boolopt/bool_optimizer.py +++ b/qlasskit/boolopt/bool_optimizer.py @@ -83,7 +83,7 @@ def apply(self, exps): return exps -bestWorkingOptimizer = BoolOptimizerProfile( +defaultOptimizer = BoolOptimizerProfile( [ merge_expressions, apply_cse, @@ -95,7 +95,7 @@ def apply(self, exps): ) -bestWorkingOptimizerDebug = BoolOptimizerProfile( +defaultOptimizerDebug = BoolOptimizerProfile( [ print_step("before"), merge_expressions, @@ -107,3 +107,13 @@ def apply(self, exps): print_step("after"), ] ) + + +fastOptimizer = BoolOptimizerProfile( + [ + remove_ITE(), + remove_Implies(), + transform_or2xor(), + transform_or2and(), + ] +) diff --git a/qlasskit/qcircuit/qcircuit.py b/qlasskit/qcircuit/qcircuit.py index 4653a6af..da39d93b 100644 --- a/qlasskit/qcircuit/qcircuit.py +++ b/qlasskit/qcircuit/qcircuit.py @@ -50,6 +50,10 @@ def get_key_by_index(self, i: int): if self.qubit_map[key] == i: return key raise Exception(f"Qubit with index {i} not found") + + def __repr__(self): + """ Return a string representation of the QCircuit """ + return f'QCircuit {self.name}\t\tGates: {self.num_gates}\t\tQubits: {self.num_qubits}' def __contains__(self, key: Union[str, Symbol, int]): """Return True if the circuit contain a qubit with a given name/symbol""" diff --git a/qlasskit/qlassfun.py b/qlasskit/qlassfun.py index be4e75d7..c8def541 100644 --- a/qlasskit/qlassfun.py +++ b/qlasskit/qlassfun.py @@ -22,7 +22,7 @@ from .ast2ast import ast2ast from .ast2logic import Arg, Args, BoolExpList, LogicFun, flatten, translate_ast -from .boolopt import BoolOptimizerProfile, bestWorkingOptimizer +from .boolopt import BoolOptimizerProfile, defaultOptimizer from .boolopt.bool_optimizer import merge_expressions from .boolquant import Q # noqa: F403, F401 from .compiler import SupportedCompiler, to_quantum @@ -187,7 +187,7 @@ def from_function( defs: List[LogicFun] = [], to_compile: bool = True, compiler: SupportedCompiler = "internal", - bool_optimizer: BoolOptimizerProfile = bestWorkingOptimizer, + bool_optimizer: BoolOptimizerProfile = defaultOptimizer, uncompute: bool = True, ) -> "QlassF": """Create a QlassF from a function or a string containing a function @@ -199,7 +199,7 @@ def from_function( 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) + (default: defaultOptimizer) uncompute (bool, optional): whenever uncompute input qubits during compilation (default: True) """ @@ -228,7 +228,7 @@ def qlassf( defs: List[QlassF] = [], to_compile: bool = True, compiler: SupportedCompiler = "internal", - bool_optimizer: BoolOptimizerProfile = bestWorkingOptimizer, + bool_optimizer: BoolOptimizerProfile = defaultOptimizer, uncompute: bool = True, ) -> QlassF: """Decorator / function creating a QlassF object @@ -240,7 +240,7 @@ def qlassf( 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) + (default: defaultOptimizer) uncompute (bool, optional): whenever uncompute input qubits during compilation (default: True) """ @@ -262,7 +262,7 @@ def qlassfa( defs: List[QlassF] = [], to_compile: bool = True, compiler: SupportedCompiler = "internal", - bool_optimizer: BoolOptimizerProfile = bestWorkingOptimizer, + bool_optimizer: BoolOptimizerProfile = defaultOptimizer, uncompute: bool = True, ): """Decorator with parameters for qlassf""" diff --git a/qlasskit/types/qint.py b/qlasskit/types/qint.py index 6f5b9aa0..7d5b0eb5 100644 --- a/qlasskit/types/qint.py +++ b/qlasskit/types/qint.py @@ -181,7 +181,7 @@ def mul(cls, tleft: TExp, tright: TExp) -> TExp: # noqa: C901 m = len(tright[1]) if n != m: - raise Exception("Mul works only on same size Qint") + raise Exception(f"Mul works only on same size Qint: {n} != {m}") product = [False] * (n + m) @@ -210,6 +210,8 @@ def mul(cls, tleft: TExp, tright: TExp) -> TExp: # noqa: C901 return Qint12, product elif (n + m) > 12 and (n + m) <= 16: return Qint16, product + elif (n + m) > 16: + return Qint16.crop((Qint16, product)) raise Exception(f"Mul result size is too big ({n+m})") diff --git a/test/test_qlassf.py b/test/test_qlassf.py index a20c128d..0faef1a7 100644 --- a/test/test_qlassf.py +++ b/test/test_qlassf.py @@ -17,7 +17,7 @@ from parameterized import parameterized_class from qlasskit import Qint, Qint2, Qint4, Qint12, QlassF, exceptions, qlassf -from qlasskit.boolopt.bool_optimizer import bestWorkingOptimizerDebug +from qlasskit.boolopt.bool_optimizer import defaultOptimizerDebug from . import utils from .utils import COMPILATION_ENABLED, ENABLED_COMPILERS, compute_and_compare_results @@ -34,7 +34,7 @@ def test_debug_optimizer(self): qf = qlassf( "def t(a: bool) -> bool: return a", to_compile=COMPILATION_ENABLED, - bool_optimizer=bestWorkingOptimizerDebug, + bool_optimizer=defaultOptimizerDebug, ) compute_and_compare_results(self, qf)