diff --git a/TODO.md b/TODO.md index aa10e3ca..0a581ded 100644 --- a/TODO.md +++ b/TODO.md @@ -108,6 +108,7 @@ - [x] Hash function preimage attack notebook - [x] Move all examples to doc - [x] Simon periodicity +- [x] Deutsch-Jozsa - [ ] Improve documentation - [ ] First stable release @@ -134,9 +135,11 @@ - [ ] Builtin function: map - [ ] First beta release -### Algorithms +### Docs + +- [ ] Simon example +- [ ] Deutsch-Jozsa example -- [ ] Deutsch-Jozsa ### Language support diff --git a/qlasskit/algorithms/__init__.py b/qlasskit/algorithms/__init__.py index 1469ffcd..d14363a9 100644 --- a/qlasskit/algorithms/__init__.py +++ b/qlasskit/algorithms/__init__.py @@ -20,3 +20,4 @@ ) from .grover import Grover # noqa: F401, E402 from .simon import Simon # noqa: F401, E402 +from .deutschjozsa import DeutschJozsa # noqa: F401, E402 diff --git a/qlasskit/algorithms/deutschjozsa.py b/qlasskit/algorithms/deutschjozsa.py new file mode 100644 index 00000000..0ff2cc17 --- /dev/null +++ b/qlasskit/algorithms/deutschjozsa.py @@ -0,0 +1,74 @@ +# 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, Union + +from ..qcircuit import QCircuit +from ..qlassfun import QlassF +from ..types import Qtype, interpret_as_qtype +from .qalgorithm import QAlgorithm + + +class DeutschJozsa(QAlgorithm): + def __init__( + self, + f: QlassF, + ): + """ + Args: + f (QlassF): our f(x) -> bool + """ + if len(f.args) != 1: + raise Exception("f should receive exactly one parameter") + if f.returns.ttype != bool: + raise Exception("f should returns bool") + + self.f: QlassF = f + self.search_space_size = len(f.args[0]) + self._qcircuit = QCircuit(self.f.num_qubits) + + self._f_circuit = self.f.circuit() + + # State preparation + self._qcircuit.barrier(label="s") + for i in range(self.search_space_size): + self._qcircuit.h(i) + self._qcircuit.x(self._f_circuit["_ret"]) + self._qcircuit.h(self._f_circuit["_ret"]) + + # Prepare and add the f + self._qcircuit.barrier(label="f") + self._qcircuit += self._f_circuit + + # State preparation out + self._qcircuit.barrier(label="s") + for i in range(self.search_space_size): + self._qcircuit.h(i) + + # @override + @property + def output_qubits(self) -> List[int]: + """Returns the list of output qubits""" + len_a = len(self.f.args[0]) + return list(range(len_a)) + + # @override + def decode_output( + self, istr: Union[str, int, List[bool]] + ) -> Union[bool, Tuple, Qtype]: + iq = interpret_as_qtype(istr, self.f.args[0].ttype, len(self.f.args[0])) + if iq == 0: + return "Constant" + else: + return "Balanced" diff --git a/qlasskit/algorithms/simon.py b/qlasskit/algorithms/simon.py index de2fdf12..2a503786 100644 --- a/qlasskit/algorithms/simon.py +++ b/qlasskit/algorithms/simon.py @@ -42,6 +42,7 @@ def __init__( self._qcircuit.h(i) # Prepare and add the f + self._qcircuit.barrier(label="f") self._qcircuit += self.f.circuit() # State preparation out diff --git a/test/test_algo_deutschjozsa.py b/test/test_algo_deutschjozsa.py new file mode 100644 index 00000000..f4a4095f --- /dev/null +++ b/test/test_algo_deutschjozsa.py @@ -0,0 +1,61 @@ +# 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 qlasskit import Qint2, Qint4, qlassf +from qlasskit.algorithms import DeutschJozsa + +from .utils import qiskit_measure_and_count + + +class TestAlgoDeutschJozsa(unittest.TestCase): + def test_deutschjozsa_balanced(self): + f = """ +def hash(k: Qint2) -> bool: + return k[0] ^ k[1] +""" + qf = qlassf(f, compiler="tweedledum") + algo = DeutschJozsa(qf) + + qc_algo = algo.circuit().export("circuit", "qiskit") + counts = qiskit_measure_and_count(qc_algo, shots=1024) + counts_readable = algo.decode_counts(counts) + self.assertEqual(counts_readable["Balanced"], 1024) + + def test_deutschjozsa_balanced2(self): + f = """ +def hash(k: bool) -> bool: + return k +""" + qf = qlassf(f, compiler="tweedledum") + algo = DeutschJozsa(qf) + + qc_algo = algo.circuit().export("circuit", "qiskit") + counts = qiskit_measure_and_count(qc_algo, shots=1024) + counts_readable = algo.decode_counts(counts) + self.assertEqual(counts_readable["Balanced"], 1024) + + def test_deutschjozsa_constant(self): + f = """ +def hash(k: Qint2) -> bool: + return False +""" + qf = qlassf(f, compiler="tweedledum") + algo = DeutschJozsa(qf) + + qc_algo = algo.circuit().export("circuit", "qiskit") + counts = qiskit_measure_and_count(qc_algo, shots=1024) + counts_readable = algo.decode_counts(counts) + self.assertEqual(counts_readable["Constant"], 1024)