diff --git a/pyqpanda-algorithm/pyqpanda_alg/QOverlap/README.md b/pyqpanda-algorithm/pyqpanda_alg/QOverlap/README.md new file mode 100644 index 00000000..4b70fec2 --- /dev/null +++ b/pyqpanda-algorithm/pyqpanda_alg/QOverlap/README.md @@ -0,0 +1,38 @@ +# QOverlap + +QOverlap provides a reusable multi-qubit SWAP Test for estimating state overlap. + +For pure states, measuring the ancilla in zero with probability P0 gives + + ||^2 = 2 * P0 - 1. + +The implementation accepts caller-supplied state-preparation circuits, so it can +compare computational-basis states, variational states, encoded feature states, +or other equal-width preparations without duplicating their construction logic. + +Example: + + from pyqpanda3.core import QCircuit, H, X + from pyqpanda_alg.QOverlap import SwapTest + + def plus_state(qubits): + circuit = QCircuit() + circuit << H(qubits[0]) + return circuit + + def one_state(qubits): + circuit = QCircuit() + circuit << X(qubits[0]) + return circuit + + result = SwapTest( + qubits_per_state=1, + prepare_left=plus_state, + prepare_right=one_state, + shots=2048, + ).run() + + print(result.overlap_squared) + +The result object also reports the measured ancilla-zero probability, overlap +magnitude, dominant ancilla bitstring, and shot count. diff --git a/pyqpanda-algorithm/pyqpanda_alg/QOverlap/SwapTest.py b/pyqpanda-algorithm/pyqpanda_alg/QOverlap/SwapTest.py new file mode 100644 index 00000000..db9d4e61 --- /dev/null +++ b/pyqpanda-algorithm/pyqpanda_alg/QOverlap/SwapTest.py @@ -0,0 +1,153 @@ +# 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. + +"""Reusable multi-qubit SWAP Test for quantum-state overlap estimation. + +For pure states |psi> and |phi>, the ancilla-zero probability is + + p(0) = (1 + ||**2) / 2, + +so the squared overlap is 2*p(0)-1. The same circuit estimates Tr(rho*sigma) +for mixed-state inputs prepared by compatible caller circuits. +""" + +from __future__ import annotations + +from dataclasses import dataclass +import math +from typing import Any, Callable, Optional, Sequence + +from pyqpanda3.core import CPUQVM, H, QProg, SWAP + + +CircuitFactory = Callable[[Sequence[Any]], Any] + + +@dataclass(frozen=True) +class SwapTestResult: + """Measurement summary returned by SwapTest.run().""" + + overlap_squared: float + overlap: float + zero_probability: float + bitstring: str + shots: int + + +def overlap_squared_from_zero_probability(zero_probability: float) -> float: + """Convert ancilla P(0) to the ideal squared state overlap. + + Shot noise or hardware noise can push the raw estimator slightly outside + the physical interval. The returned value is therefore clamped to [0, 1]. + """ + + if isinstance(zero_probability, bool) or not isinstance( + zero_probability, (int, float) + ): + raise ValueError("zero_probability must be a finite real number") + + zero_probability = float(zero_probability) + if not math.isfinite(zero_probability): + raise ValueError("zero_probability must be a finite real number") + if zero_probability < 0.0 or zero_probability > 1.0: + raise ValueError("zero_probability must be in [0, 1]") + + return min(1.0, max(0.0, 2.0 * zero_probability - 1.0)) + + +class SwapTest: + """Estimate overlap between two equally sized prepared quantum states. + + Parameters + ---------- + qubits_per_state: + Number of qubits in each state register. + prepare_left: + Callable taking the left register and returning its preparation circuit. + prepare_right: + Callable taking the right register and returning its preparation circuit. + shots: + Default number of CPU-simulator shots for run(). + + The implementation uses one ancilla plus two equal-width state registers. + Each corresponding register pair is joined by a SWAP gate controlled by + the ancilla, giving the standard multi-qubit SWAP Test without imposing a + specific state-preparation method on callers. + """ + + def __init__( + self, + qubits_per_state: int, + prepare_left: CircuitFactory, + prepare_right: CircuitFactory, + shots: int = 1024, + ): + if isinstance(qubits_per_state, bool) or not isinstance(qubits_per_state, int): + raise ValueError("qubits_per_state must be an integer") + if qubits_per_state <= 0: + raise ValueError("qubits_per_state must be positive") + if not callable(prepare_left) or not callable(prepare_right): + raise ValueError("prepare_left and prepare_right must be callable") + if isinstance(shots, bool) or not isinstance(shots, int) or shots <= 0: + raise ValueError("shots must be a positive integer") + + self.qubits_per_state = qubits_per_state + self.prepare_left = prepare_left + self.prepare_right = prepare_right + self.shots = shots + + def build_program(self): + """Build the SWAP-test program and return its logical registers.""" + + qubits = QProg(1 + 2 * self.qubits_per_state).qubits() + ancilla = qubits[0] + left = qubits[1 : 1 + self.qubits_per_state] + right = qubits[1 + self.qubits_per_state :] + + program = QProg() + program << self.prepare_left(left) + program << self.prepare_right(right) + program << H(ancilla) + + for left_qubit, right_qubit in zip(left, right): + program << SWAP(left_qubit, right_qubit).control([ancilla]) + + program << H(ancilla) + return program, ancilla, left, right + + def run(self, shots: Optional[int] = None) -> SwapTestResult: + """Run the SWAP Test on the CPU simulator.""" + + if shots is None: + shots = self.shots + if isinstance(shots, bool) or not isinstance(shots, int) or shots <= 0: + raise ValueError("shots must be a positive integer") + + program, ancilla, _, _ = self.build_program() + machine = CPUQVM() + machine.run(program, shots) + probabilities = machine.result().get_prob_dict([ancilla]) + + if not probabilities: + raise RuntimeError("SWAP Test returned no ancilla probabilities") + + zero_probability = float(probabilities.get("0", 0.0)) + overlap_squared = overlap_squared_from_zero_probability(zero_probability) + bitstring = max(probabilities, key=probabilities.get) + + return SwapTestResult( + overlap_squared=overlap_squared, + overlap=math.sqrt(overlap_squared), + zero_probability=zero_probability, + bitstring=bitstring, + shots=shots, + ) diff --git a/pyqpanda-algorithm/pyqpanda_alg/QOverlap/__init__.py b/pyqpanda-algorithm/pyqpanda_alg/QOverlap/__init__.py new file mode 100644 index 00000000..7f1fcf7e --- /dev/null +++ b/pyqpanda-algorithm/pyqpanda_alg/QOverlap/__init__.py @@ -0,0 +1,9 @@ +"""Quantum-state overlap estimation with the SWAP Test.""" + +from .SwapTest import SwapTest, SwapTestResult, overlap_squared_from_zero_probability + +__all__ = [ + "SwapTest", + "SwapTestResult", + "overlap_squared_from_zero_probability", +] diff --git a/pyqpanda-algorithm/pyqpanda_alg/__init__.py b/pyqpanda-algorithm/pyqpanda_alg/__init__.py index 12d68083..16cbf29d 100644 --- a/pyqpanda-algorithm/pyqpanda_alg/__init__.py +++ b/pyqpanda-algorithm/pyqpanda_alg/__init__.py @@ -1,54 +1,55 @@ -''' -A Quantum Optimization Algorithm Set, based on pyqpanda. - -The **QMengJi** is a collection of fundamental quantum optimization algorithms and -functions that are commonly used in developer's optimization problems. - -The QMengJi provides standardized set of tools and building blocks for writing quantum programs. - -Some key functions included in the QMengJi are: - -Quantum Approximate Optimization Algorithm : This is a quantum algorithm that can be used to search the lowest energy -eigenstate of a Hamiltonian. It is considered to be one of the candidate algorithms for quantum advantage. -Developer can construct object by QAOA(problem, arg1, arg2…) and directly call QAOA.run(args) to optimize their -user-defined problem. - -Overall, the QMengJi provides a standardized set of tools for developers, allowing them to optimize functions with -binary variables, such as combinatorial optimization problems, that may have many practical implications. -Developers can use quantum algorithms directly to obtain results without knowing anything about quantum computing, or -build their own suitable quantum circuits to obtain more customized results according to their requirements.It is an -important resource for solving optimization problems and advancing research on quantum optimization algorithms. - -''' -''' -pyqpanda-algorithm Python\n -Copyright (C) Origin Quantum 2017-2023\n -Licensed Under Apache Licence 2.0 -''' - -import warnings -warnings.filterwarnings("ignore", category=SyntaxWarning) - -from . import QAOA -# from . import VQE -# from . import HHL -# from . import QAOA -from . import QARM -from . import QKmeans -# from . import QLuoShu -from . import QPCA -# from . import QSolver -from . import QSVM -# from . import extensions -# import warnings - -#QFinance -from . import QUBO -from . import QCmp -from . import QAE -from . import QSVD -from . import QSVR -from . import Grover -from . import QmRMR -from . import QSEncode - +''' +A Quantum Optimization Algorithm Set, based on pyqpanda. + +The **QMengJi** is a collection of fundamental quantum optimization algorithms and +functions that are commonly used in developer's optimization problems. + +The QMengJi provides standardized set of tools and building blocks for writing quantum programs. + +Some key functions included in the QMengJi are: + +Quantum Approximate Optimization Algorithm : This is a quantum algorithm that can be used to search the lowest energy +eigenstate of a Hamiltonian. It is considered to be one of the candidate algorithms for quantum advantage. +Developer can construct object by QAOA(problem, arg1, arg2…) and directly call QAOA.run(args) to optimize their +user-defined problem. + +Overall, the QMengJi provides a standardized set of tools for developers, allowing them to optimize functions with +binary variables, such as combinatorial optimization problems, that may have many practical implications. +Developers can use quantum algorithms directly to obtain results without knowing anything about quantum computing, or +build their own suitable quantum circuits to obtain more customized results according to their requirements.It is an +important resource for solving optimization problems and advancing research on quantum optimization algorithms. + +''' +''' +pyqpanda-algorithm Python\n +Copyright (C) Origin Quantum 2017-2023\n +Licensed Under Apache Licence 2.0 +''' + +import warnings +warnings.filterwarnings("ignore", category=SyntaxWarning) + +from . import QAOA +# from . import VQE +# from . import HHL +# from . import QAOA +from . import QARM +from . import QKmeans +# from . import QLuoShu +from . import QPCA +# from . import QSolver +from . import QSVM +# from . import extensions +# import warnings + +#QFinance +from . import QUBO +from . import QCmp +from . import QAE +from . import QSVD +from . import QSVR +from . import Grover +from . import QOverlap +from . import QmRMR +from . import QSEncode + diff --git a/test/QOverlap/Test_swap_test.py b/test/QOverlap/Test_swap_test.py new file mode 100644 index 00000000..9c2fb16f --- /dev/null +++ b/test/QOverlap/Test_swap_test.py @@ -0,0 +1,24 @@ +import math + +import pytest + +from pyqpanda_alg.QOverlap import overlap_squared_from_zero_probability + + +@pytest.mark.parametrize( + ("zero_probability", "expected"), + [(0.5, 0.0), (0.75, 0.5), (1.0, 1.0), (0.4, 0.0)], +) +def test_overlap_squared_from_zero_probability(zero_probability, expected): + assert math.isclose( + overlap_squared_from_zero_probability(zero_probability), + expected, + rel_tol=0.0, + abs_tol=1e-12, + ) + + +@pytest.mark.parametrize("value", [-0.01, 1.01, float("nan"), float("inf")]) +def test_overlap_decoder_rejects_invalid_probability(value): + with pytest.raises(ValueError): + overlap_squared_from_zero_probability(value) diff --git a/test/pytest.ini b/test/pytest.ini index 2887f64b..c7ed5b0b 100644 --- a/test/pytest.ini +++ b/test/pytest.ini @@ -1,20 +1,21 @@ -[pytest] -testpaths = - QAlgBase - QAOA - QRAM - QPCA - QSVM - - -python_files = - test_*.py - Test_*.py - -addopts = -vs --alluredir=./allure-results --clean-alluredir - - -log_level = INFO - - +[pytest] +testpaths = + QAlgBase + QAOA + QOverlap + QRAM + QPCA + QSVM + + +python_files = + test_*.py + Test_*.py + +addopts = -vs --alluredir=./allure-results --clean-alluredir + + +log_level = INFO + + log_format = %(asctime)s - %(name)s - %(levelname)s - %(message)s \ No newline at end of file