diff --git a/TODO.md b/TODO.md index 2de2a153..d8426b97 100644 --- a/TODO.md +++ b/TODO.md @@ -23,7 +23,7 @@ - [x] Ast2logic: fix ret_type for multiple results - [x] QlassF: truth table creation - [x] Quantum circuit abstraction -- [ ] Extend testing to compilation +- [x] Extend testing to compilation - [ ] Int arithmetic expressions - [ ] Compiler: prepare invertible abstraction diff --git a/qlasskit/ast2logic/t_statement.py b/qlasskit/ast2logic/t_statement.py index c5376b84..76e1db9e 100644 --- a/qlasskit/ast2logic/t_statement.py +++ b/qlasskit/ast2logic/t_statement.py @@ -68,7 +68,6 @@ def translate_statement( # noqa: C901 vexp = translate_expression(stmt.value, env) res, env = type_of_exp(vexp, "_ret", env) res = list(map(lambda x: (Symbol(x[0]), x[1]), res)) - print(res) return res, env # FunctionDef diff --git a/qlasskit/qlassf.py b/qlasskit/qlassf.py index 520b8d11..658ed074 100644 --- a/qlasskit/qlassf.py +++ b/qlasskit/qlassf.py @@ -100,7 +100,7 @@ def gate(self, framework="qiskit"): raise Exception("Not yet compiled") if framework == "qiskit": - g = self._compiled_gate.to_qiskit() + g = self._compiled_gate.export(mode="gate", framework=framework) g.name = self.name return g else: @@ -122,7 +122,14 @@ def res_qubits(self) -> List[int]: @property def num_qubits(self) -> int: """Return the number of qubits""" - return len(self.qubits()) + if self._compiled_gate is None: + raise Exception("Not yet compiled") + return self._compiled_gate.num_qubits + + @property + def input_size(self) -> int: + """Return the size of the inputs""" + return len(self.args) def bind(self, **kwargs) -> "QlassF": """Returns a new QlassF with defined params""" diff --git a/qlasskit/utils.py b/qlasskit/utils.py index 0ca1c732..a54e9c55 100644 --- a/qlasskit/utils.py +++ b/qlasskit/utils.py @@ -13,6 +13,31 @@ # limitations under the License. from typing import Any, List +from sympy.logic.boolalg import And, BooleanFunction, Xor + + +class BToffoli(BooleanFunction): + """ + Logical Toffoli function; invert the third argument, if the first two are True. + + c XOR (a AND b) + + Examples + ======== + + >>> BToffoli(False, False, True) + True + >>> BToffoli(True, True, True) + False + >>> BToffoli(True, True, False) + True + + """ + + @classmethod + def eval(cls, a, b, c): + return Xor(c, And(a, b)) + def flatten(m: List[List[Any]]) -> List[Any]: return [item for row in m for item in row] diff --git a/test/test_compiler.py b/test/test_compiler.py new file mode 100644 index 00000000..ab4e9137 --- /dev/null +++ b/test/test_compiler.py @@ -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. + +import unittest + +from qiskit import QuantumCircuit + +from qlasskit import exceptions, qlassf + +from .utils import compare_circuit_truth_table, qiskit_measure_and_count + + +class TestCompiler(unittest.TestCase): + def test_not_arg(self): + f = "def test(a: bool) -> bool:\n\treturn not a" + qf = qlassf(f, to_compile=True) + compare_circuit_truth_table(self, qf) + + def test_and(self): + f = "def test(a: bool, b: bool) -> bool:\n\treturn a and b" + qf = qlassf(f, to_compile=True) + compare_circuit_truth_table(self, qf) + + def test_or(self): + f = "def test(a: bool, b: bool) -> bool:\n\treturn a or b" + qf = qlassf(f, to_compile=True) + compare_circuit_truth_table(self, qf) diff --git a/test/utils.py b/test/utils.py index e57e2c3a..30eaaf2b 100644 --- a/test/utils.py +++ b/test/utils.py @@ -41,3 +41,26 @@ def qiskit_measure_and_count(circ): result = simulator.run(circ).result() counts = result.get_counts(circ) return counts + + +def compare_circuit_truth_table(cls, qf): + truth_table = qf.truth_table() + gate = qf.gate() + + for truth_line in truth_table: + qc = QuantumCircuit(gate.num_qubits) + + # Prepare inputs + for i in range(qf.input_size): + qc.initialize(1 if truth_line[i] else 0, i) + + qc.append(gate, list(range(qf.num_qubits))) + + # print(qc.draw("text")) + counts = qiskit_measure_and_count(qc) + truth_str = "".join( + map(lambda x: "1" if x else "0", truth_line[-qf.ret_size :]) + ) + + cls.assertEqual(len(counts), 1) + cls.assertEqual(truth_str, list(counts.keys())[0][0 : len(truth_str)])