diff --git a/pyqpanda-algorithm/pyqpanda_alg/QAE/QAE.py b/pyqpanda-algorithm/pyqpanda_alg/QAE/QAE.py index e126c0e..88c4a0f 100644 --- a/pyqpanda-algorithm/pyqpanda_alg/QAE/QAE.py +++ b/pyqpanda-algorithm/pyqpanda_alg/QAE/QAE.py @@ -79,36 +79,30 @@ def __init__(self, operator_in=None, if type(self.res_index) != list: self.res_index = [self.res_index] - def __del__(self): - pass - - def _Q_cir(self, q_operator): - if type(self.res_index) != list: - self.res_index = [self.res_index] - q_target = [] - q_else = [] - q1 = [] - index = [] - for i in self.res_index: - index += [(list(range(self.qnumber)))[i]] - for i in range(self.qnumber): - if i in index: - q_target += [q_operator[i]] - else: - q_else += [q_operator[i]] - if i != index[-1]: - q1 += [q_operator[i]] + def __del__(self): + pass + + def _target_indices(self): + return [(list(range(self.qnumber)))[i] for i in self.res_index] + + def _Q_cir(self, q_operator): + index = self._target_indices() + q_target = [q_operator[i] for i in index] + q1 = [] + for i in range(self.qnumber): + if i != index[-1]: + q1 += [q_operator[i]] Qcir = QCircuit() if len(q_target) == 1: Qcir << Z(q_target[0]) << RX(q_target[0], 2 * pi) elif len(q_target) > 1: Qcir << Z(q_target[-1]).control(q_target[:-1]) << RX(q_target[-1], 2 * pi) - - for k in range(len(self.target_state)): - if self.target_state[k] == '0': - Qcir << X(q_target[-k-1]) - Qcir << self.operator(q_operator).dagger() + + for k in range(len(self.target_state)): + if self.target_state[k] == '0': + Qcir << X(q_target[k]) + Qcir << self.operator(q_operator).dagger() for q1idx in q1: Qcir << X(q1idx) @@ -117,11 +111,11 @@ def _Q_cir(self, q_operator): for q1idx in q1: Qcir << X(q1idx) - Qcir << self.operator(q_operator) - for k in range(len(self.target_state)): - if self.target_state[k] == '0': - Qcir << X(q_target[-k-1]) - return Qcir + Qcir << self.operator(q_operator) + for k in range(len(self.target_state)): + if self.target_state[k] == '0': + Qcir << X(q_target[k]) + return Qcir def run(self): """ @@ -154,20 +148,15 @@ def run(self): """ q_operator = QProg(self.qnumber + self.n_anc).qubits() - q_target = [] - index = [] - for i in self.res_index: - index += [(list(range(self.qnumber)))[i]] - for i in range(self.qnumber): - if i in index: - q_target += [q_operator[i]] + index = self._target_indices() + q_target = [q_operator[i] for i in index] prog = QProg() - prog << self.operator(q_operator[:self.qnumber]) - for k in range(len(self.target_state)): - if self.target_state[k] == '0': - prog << X(q_target[-k-1]) + prog << self.operator(q_operator[:self.qnumber]) + for k in range(len(self.target_state)): + if self.target_state[k] == '0': + prog << X(q_target[k]) for qidx in q_operator[self.qnumber:]: prog << H(qidx) @@ -229,9 +218,9 @@ def __init__(self, operator_in=None, self.alpha = alpha self.method = method self.ratio = ratio - self.qnumber = qnumber - self.res_index = res_index - self.machine_type = machine_type + self.qnumber = qnumber + self.res_index = (list(range(self.qnumber)))[res_index] + self.machine_type = machine_type self.n_sum = 0 if machine_type == 'CPU': @@ -349,16 +338,18 @@ def run(self): def _measure(self, k: int, n_round: int) -> int: machine = self.machine qlist = self.qlist - clist = self.clist - - operator_g = amp_operator(in_operator=self.operatorA, q_input=qlist) + operator_g = amp_operator( + in_operator=self.operatorA, + q_input=qlist, + q_flip=[qlist[self.res_index]], + ) prog = QProg() prog << self.operatorA(qlist) for i in range(k): prog << operator_g if self.draw: print(prog) - prog << measure_all([qlist[self.res_index]], [qlist[self.qnumber - 1]]) + prog << measure_all([qlist[self.res_index]], [0]) if self.machine_type == 'CPU': machine.run(prog, n_round) res = machine.result().get_counts() diff --git a/test/QAlgBase/Test_QAE_IQAE.py b/test/QAlgBase/Test_QAE_IQAE.py index a914e29..90dbc98 100644 --- a/test/QAlgBase/Test_QAE_IQAE.py +++ b/test/QAlgBase/Test_QAE_IQAE.py @@ -1,30 +1,44 @@ -# import pytest -# import numpy as np -# from pyqpanda_alg.QAE.QAE import IQAE -# from pyqpanda3.core import QCircuit, RY, X, RZ -# -# -# class Test_QAE_IQAE: -# -# def create_cir_basic(self, qlist): -# cir = QCircuit() -# cir << RY(qlist[0], np.pi / 3) << X(qlist[1]).control(qlist[0]) -# return cir -# -# def test_iqae_basic_functionality(self): -# W = IQAE( -# operator_in=self.create_cir_basic, -# qnumber=2, -# epsilon=0.01, -# res_index=-1 -# ).run() -# -# # 断言验证 -# assert W is not None, "振幅估计结果不应为None" -# assert isinstance(W, (float, np.floating)), f"振幅应为数值类型,实际为{type(W)}" -# assert 0.2 <= W <= 0.3, f"振幅应在[0,1]范围内,实际为{W}" -# -# -# if __name__ == "__main__": -# # 运行测试 -# pytest.main([__file__, "-v", "-s"]) +import numpy as np +from pyqpanda3.core import QCircuit, RY + +from pyqpanda_alg.QAE import IQAE + + +def asymmetric_two_qubit_state(qlist): + cir = QCircuit() + cir << RY(qlist[0], np.pi / 5) + cir << RY(qlist[1], np.pi / 3) + return cir + + +def test_iqae_estimates_non_last_result_qubit(): + prob = IQAE( + operator_in=asymmetric_two_qubit_state, + qnumber=2, + epsilon=0.005, + res_index=0, + ).run() + + assert np.isclose(prob, np.sin(np.pi / 10) ** 2, atol=0.08) + + +def test_iqae_estimates_last_result_qubit(): + prob = IQAE( + operator_in=asymmetric_two_qubit_state, + qnumber=2, + epsilon=0.005, + res_index=1, + ).run() + + assert np.isclose(prob, np.sin(np.pi / 6) ** 2, atol=0.08) + + +def test_iqae_negative_index_keeps_last_qubit_default(): + prob = IQAE( + operator_in=asymmetric_two_qubit_state, + qnumber=2, + epsilon=0.005, + res_index=-1, + ).run() + + assert np.isclose(prob, np.sin(np.pi / 6) ** 2, atol=0.08) diff --git a/test/QAlgBase/Test_QAE_QAE.py b/test/QAlgBase/Test_QAE_QAE.py index 64ca585..ad1ec8e 100644 --- a/test/QAlgBase/Test_QAE_QAE.py +++ b/test/QAlgBase/Test_QAE_QAE.py @@ -1,35 +1,74 @@ -# import math -# -# import pytest -# import numpy as np -# from pyqpanda_alg.QAE import QAE -# from pyqpanda3.core import QCircuit, RY, X, H -# -# -# def create_cir_basic(qlist): -# """基础测试量子线路""" -# cir = QCircuit() -# cir << RY(qlist[0], np.pi / 3) << X(qlist[1]).control(qlist[0]) -# return cir -# -# -# class Test_QAE_QAE: -# -# def test_qae_basic_functionality(self): -# W = QAE( -# operator_in=create_cir_basic, -# qnumber=2, -# epsilon=0.01, -# res_index=[0, 1], -# target_state='11' -# ).run() -# -# assert W is not None, "振幅估计结果不应为None" -# assert isinstance(W, (float, np.floating)), f"振幅应为数值类型,实际为{type(W)}" -# assert 0.2 <= W <= 0.3, f"振幅应在[0,1]范围内,实际为{W}" -# -# -# -# if __name__ == "__main__": -# # 运行所有测试 -# pytest.main([__file__, "-v", "-s"]) +import numpy as np +from pyqpanda3.core import QCircuit, RY + +from pyqpanda_alg.QAE import QAE + + +def single_qubit_state(qlist): + cir = QCircuit() + cir << RY(qlist[0], np.pi / 3) + return cir + + +def asymmetric_two_qubit_state(qlist): + cir = QCircuit() + cir << RY(qlist[0], np.pi / 3) + cir << RY(qlist[1], np.pi / 5) + return cir + + +def test_qae_single_qubit_target_states(): + prob_one = QAE( + operator_in=single_qubit_state, + qnumber=1, + epsilon=0.005, + res_index=0, + target_state="1", + ).run() + prob_zero = QAE( + operator_in=single_qubit_state, + qnumber=1, + epsilon=0.005, + res_index=0, + target_state="0", + ).run() + + assert np.isclose(prob_one, 0.25, atol=0.02) + assert np.isclose(prob_zero, 0.75, atol=0.02) + + +def test_qae_target_state_follows_res_index_order(): + p0 = np.sin(np.pi / 6) ** 2 + p1 = np.sin(np.pi / 10) ** 2 + expected = { + "00": (1 - p0) * (1 - p1), + "01": (1 - p0) * p1, + "10": p0 * (1 - p1), + "11": p0 * p1, + } + + for target_state, target_prob in expected.items(): + prob = QAE( + operator_in=asymmetric_two_qubit_state, + qnumber=2, + epsilon=0.005, + res_index=[0, 1], + target_state=target_state, + ).run() + + assert np.isclose(prob, target_prob, atol=0.02) + + +def test_qae_non_natural_res_index_order(): + p0 = np.sin(np.pi / 6) ** 2 + p1 = np.sin(np.pi / 10) ** 2 + + prob = QAE( + operator_in=asymmetric_two_qubit_state, + qnumber=2, + epsilon=0.005, + res_index=[1, 0], + target_state="01", + ).run() + + assert np.isclose(prob, (1 - p1) * p0, atol=0.02)