Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ pyqpanda-algorithm 是由本源量子(Origin Quantum)开发的量子算法
- **QSVD(量子变分奇异值分解)**
在变分框架下提取矩阵的奇异值与奇异向量,用于降维与推荐系统。

- **QPE(量子相位估计)**
通过受控酉演化与逆量子傅里叶变换估计酉算符本征相位,并支持将相位换算为哈密顿量本征值。

### 4. **通用工具与基础组件**

提供量子振幅估计算法、比较器、稀疏编码等底层工具。
Expand Down
2 changes: 2 additions & 0 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ Integrates quantum computing into classical machine learning to improve efficien
Solves key problems in physical modeling and engineering simulation (e.g., eigenvalues, linear equations, matrix decomposition).
- **QSVD (Quantum Variational Singular Value Decomposition)**
Extracts matrix singular values/vectors under a variational framework (for dimensionality reduction and recommendation systems).
- **QPE (Quantum Phase Estimation)**
Estimates eigenphases with controlled unitary evolution and inverse QFT, with helpers for converting phases to Hamiltonian eigenvalues.

### 4. General Tools & Basic Components
Provides underlying tools for quantum computing workflows.
Expand Down
34 changes: 34 additions & 0 deletions pyqpanda-algorithm/example/QAlgBase/testeg_QPE.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Minimal Quantum Phase Estimation example."""

from math import pi

from pyqpanda3.core import QCircuit, U1, X

from pyqpanda_alg.QPE import QPE


TARGET_PHASE = 3.0 / 8.0


def prepare_one(qubits):
circuit = QCircuit()
circuit << X(qubits[0])
return circuit


def phase_unitary(qubits):
circuit = QCircuit()
circuit << U1(qubits[0], 2.0 * pi * TARGET_PHASE)
return circuit


result = QPE(
unitary=phase_unitary,
system_qubits=1,
precision_qubits=5,
eigenstate_preparation=prepare_one,
).run()

print("phase:", result.phase)
print("bitstring:", result.bitstring)
print("probability:", result.probability)
10 changes: 10 additions & 0 deletions pyqpanda-algorithm/pyqpanda_alg/QPE/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""Quantum Phase Estimation public API."""

from .qpe import QPE, PhaseEstimationResult, decode_phase, phase_to_eigenvalue

__all__ = [
"QPE",
"PhaseEstimationResult",
"decode_phase",
"phase_to_eigenvalue",
]
170 changes: 170 additions & 0 deletions pyqpanda-algorithm/pyqpanda_alg/QPE/qpe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# 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.

"""Quantum Phase Estimation for PyQPanda3 circuits."""

from dataclasses import dataclass
from math import pi
from typing import Callable, Dict, Optional

from pyqpanda3.core import CPUQVM, H, QCircuit, QProg

from ..plugin import QFT


@dataclass(frozen=True)
class PhaseEstimationResult:
"""Quantum phase estimation result."""

phase: float
bitstring: str
probability: float
resolution: float
probabilities: Dict[str, float]


def decode_phase(probabilities: Dict[str, float], precision_qubits: int) -> PhaseEstimationResult:
"""Decode the most likely phase from a counting-register distribution."""
if precision_qubits < 1:
raise ValueError("precision_qubits must be at least 1")
if not probabilities:
raise ValueError("probabilities cannot be empty")

bitstring = max(probabilities, key=probabilities.get)
if len(bitstring) != precision_qubits or any(bit not in "01" for bit in bitstring):
raise ValueError(
f"expected {precision_qubits}-bit binary keys, got {bitstring!r}"
)

denominator = 1 << precision_qubits
return PhaseEstimationResult(
phase=int(bitstring, 2) / denominator,
bitstring=bitstring,
probability=float(probabilities[bitstring]),
resolution=1.0 / denominator,
probabilities=dict(probabilities),
)


def phase_to_eigenvalue(
phase: float,
evolution_time: float = 1.0,
centered: bool = False,
) -> float:
"""Convert a phase to an eigenvalue for U = exp(i * H * t)."""
if not 0.0 <= phase < 1.0:
raise ValueError("phase must lie in [0, 1)")
if evolution_time <= 0:
raise ValueError("evolution_time must be positive")

wrapped = phase - 1.0 if centered and phase >= 0.5 else phase
return 2.0 * pi * wrapped / evolution_time


class QPE:
"""Standard Quantum Phase Estimation.

The unitary callable receives the system-qubit list and returns a QCircuit
implementing U. eigenstate_preparation may prepare an eigenstate before
phase kickback.

For algorithms that can construct powers of U more efficiently than
repeating the base circuit, unitary_power(qubits, power) may be supplied
and must return a circuit implementing U raised to power.
"""

def __init__(
self,
unitary: Callable[[list[int]], QCircuit],
system_qubits: int,
precision_qubits: int = 5,
eigenstate_preparation: Optional[Callable[[list[int]], QCircuit]] = None,
unitary_power: Optional[Callable[[list[int], int], QCircuit]] = None,
shots: int = 2048,
machine=None,
) -> None:
if not callable(unitary):
raise TypeError("unitary must be callable")
if eigenstate_preparation is not None and not callable(eigenstate_preparation):
raise TypeError("eigenstate_preparation must be callable")
if unitary_power is not None and not callable(unitary_power):
raise TypeError("unitary_power must be callable")
if system_qubits < 1:
raise ValueError("system_qubits must be at least 1")
if precision_qubits < 1:
raise ValueError("precision_qubits must be at least 1")
if shots < 1:
raise ValueError("shots must be at least 1")

self.unitary = unitary
self.system_qubits = system_qubits
self.precision_qubits = precision_qubits
self.eigenstate_preparation = eigenstate_preparation
self.unitary_power = unitary_power
self.shots = shots
self.machine = machine if machine is not None else CPUQVM()

def _append_controlled_power(
self,
program: QProg,
system_register: list[int],
control_qubit: int,
power: int,
) -> None:
if self.unitary_power is not None:
powered = self.unitary_power(system_register, power)
if not isinstance(powered, QCircuit):
raise TypeError("unitary_power must return QCircuit")
program << powered.control([control_qubit])
return

for _ in range(power):
base = self.unitary(system_register)
if not isinstance(base, QCircuit):
raise TypeError("unitary must return QCircuit")
program << base.control([control_qubit])

def build_program(self) -> tuple[QProg, list[int]]:
"""Build QPE and return the program plus its counting register."""
qubits = QProg(self.system_qubits + self.precision_qubits).qubits()
system_register = qubits[: self.system_qubits]
counting_register = qubits[self.system_qubits :]

program = QProg()
if self.eigenstate_preparation is not None:
preparation = self.eigenstate_preparation(system_register)
if not isinstance(preparation, QCircuit):
raise TypeError("eigenstate_preparation must return QCircuit")
program << preparation

for qubit in counting_register:
program << H(qubit)

# Match the register-power convention already used by QAE: counting
# qubit i controls U^(2^i), followed by inverse QFT.
for index, control in enumerate(counting_register):
self._append_controlled_power(
program,
system_register,
control,
1 << index,
)

program << QFT(counting_register).dagger()
return program, counting_register

def run(self) -> PhaseEstimationResult:
"""Execute QPE and return the dominant phase and full distribution."""
program, counting_register = self.build_program()
self.machine.run(program, self.shots)
probabilities = self.machine.result().get_prob_dict(counting_register)
return decode_phase(probabilities, self.precision_qubits)
1 change: 1 addition & 0 deletions pyqpanda-algorithm/pyqpanda_alg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
from . import QUBO
from . import QCmp
from . import QAE
from . import QPE
from . import QSVD
from . import QSVR
from . import Grover
Expand Down
1 change: 1 addition & 0 deletions pyqpanda-algorithm/setup-cython.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ def run(self):
self.copy_file(Path('pyqpanda_alg/HHL') / '__init__.py', root_dir, target_dir)
self.copy_file(Path('pyqpanda_alg/QCPA') / '__init__.py', root_dir, target_dir)
self.copy_file(Path('pyqpanda_alg/QCmp') / '__init__.py', root_dir, target_dir)
self.copy_file(Path('pyqpanda_alg/QPE') / '__init__.py', root_dir, target_dir)
self.copy_file(Path('pyqpanda_alg/Grover') / '__init__.py', root_dir, target_dir)

remove_all(target_dir)
Expand Down