diff --git a/mindquantum/algorithm/library/amplitude_encoder.py b/mindquantum/algorithm/library/amplitude_encoder.py index 97deb1b19fdeca29c836b4858234848d80dc0559..36958dc6159e91076373ceedf0c16f5ded0db02f 100644 --- a/mindquantum/algorithm/library/amplitude_encoder.py +++ b/mindquantum/algorithm/library/amplitude_encoder.py @@ -14,31 +14,35 @@ # ============================================================================ """Amplitude encoder for quantum machine learning.""" -import math - import numpy as np - from mindquantum.core.circuit import Circuit -from mindquantum.core.gates import RY, X +from mindquantum.core.gates import RY, RZ, X from mindquantum.core.parameterresolver import ParameterResolver from mindquantum.utils.type_value_check import _check_input_type - - -def controlled_gate(circuit, gate, t_qubit, c_qubits, zero_qubit): - """Add an extended quantum controlled gate.""" - tmp = [] - for i, control in enumerate(c_qubits): - tmp.append(control) - if control < 0 or (control == 0 and zero_qubit == 0): - circuit += X.on(abs(control)) - tmp[i] = -tmp[i] - - circuit += gate.on(t_qubit, tmp) - - for control in c_qubits: - if control < 0 or (control == 0 and zero_qubit == 0): - circuit += X.on(abs(control)) - +from mindquantum.utils import normalize + +def amp_circuit(n_qubits): + '''Construct the quantum circuit of the amplitude_encoder.''' + circ = Circuit() + circ += RY(f'alpha_{n_qubits}0').on(n_qubits-1) + circ += RZ(f'lambda_{n_qubits}0').on(n_qubits-1) + for i in range(1, n_qubits): + for j in range(int(2**i)): + string = bin(j)[2:].zfill(i) + for tem_qubit, bit in enumerate(string): + qubit = int(n_qubits - tem_qubit) + if bit == '0': + circ += X.on(qubit-1) + + circ += RY(f'alpha_{int(n_qubits-i)}{j}').on(int(n_qubits-1-i), list(range(n_qubits-i, n_qubits))) + circ += RZ(f'lambda_{int(n_qubits-i)}{j}').on(int(n_qubits-1-i), list(range(n_qubits-i, n_qubits))) + string = bin(j)[2:].zfill(i) + + for tem_qubit, bit in enumerate(string): + qubit = int(n_qubits - tem_qubit) + if bit == '0': + circ += X.on(qubit-1) + return circ # pylint: disable=too-many-locals def amplitude_encoder(x, n_qubits): @@ -61,22 +65,23 @@ def amplitude_encoder(x, n_qubits): Examples: >>> from mindquantum.algorithm.library import amplitude_encoder >>> from mindquantum.simulator import Simulator - >>> sim = Simulator('mqvector', 8) - >>> encoder, parameterResolver = amplitude_encoder([0.5, -0.5, 0.5, 0.5], 8) + >>> sim = Simulator('mqvector', 2) + >>> encoder, parameterResolver = amplitude_encoder([0.5, -0.5, -0.5j, -0.5j], 2) >>> sim.apply_circuit(encoder, parameterResolver) >>> print(sim.get_qs(True)) - 1/2¦00000000⟩ - -1/2¦00000001⟩ - 1/2¦00000010⟩ - 1/2¦00000011⟩ + 1/2¦00⟩ + -1/2¦01⟩ + -1/2j¦10⟩ + -1/2j¦11⟩ >>> sim.reset() - >>> encoder, parameterResolver = amplitude_encoder([0, 0, 0.5, 0.5, -0.5, 0.5], 8) + >>> encoder, parameterResolver = amplitude_encoder([0, -0.5j, -0.5j, -0.5, 0.5], 3) + >>> sim = Simulator('mqvector', 3) >>> sim.apply_circuit(encoder, parameterResolver) >>> print(sim.get_qs(True)) - 1/2¦00000010⟩ - 1/2¦00000011⟩ - -1/2¦00000100⟩ - 1/2¦00000101⟩ + -1/2j¦001⟩ + -1/2j¦010⟩ + -1/2¦011⟩ + 1/2¦100⟩ """ _check_input_type('amplitude_encoder', (np.ndarray, list), x) _check_input_type('n_qubits', (int), n_qubits) @@ -87,37 +92,42 @@ def amplitude_encoder(x, n_qubits): while 2**n_qubits != len(x): x.append(0) - circuit = Circuit() - tree = [] - for i in range(len(x) - 1): - tree.append(0) - for x_val in x: - tree.append(x_val) - for i in range(len(x) - 2, -1, -1): - tree[i] += math.sqrt(tree[i * 2 + 1] * tree[i * 2 + 1] + tree[i * 2 + 2] * tree[i * 2 + 2]) - - path = [[]] - num = {} - cnt = 0 - for i in range(1, 2 * len(x) - 1, 2): - path.append(path[(i - 1) // 2] + [-1]) - path.append(path[(i - 1) // 2] + [1]) - - tmp = path[(i - 1) // 2] - controls = [] - for j, tmp_j in enumerate(tmp): - controls.append(tmp_j * j) - theta = 0 - if tree[(i - 1) // 2] > 1e-10: - try: - amp_0 = tree[i] / tree[(i - 1) // 2] - except ZeroDivisionError as exc: - raise ZeroDivisionError("Failed to set amplitude encoding.") from exc - theta = 2 * math.acos(amp_0) - if tree[i + 1] < 0 < math.sin(theta / 2): - theta = -theta - num[f'alpha{cnt}'] = theta - controlled_gate(circuit, RY(f'alpha{cnt}'), len(tmp), controls, (0 if tmp and tmp[0] == -1 else 1)) - cnt += 1 - - return circuit.reverse_qubits(), ParameterResolver(num) + vec = normalize(x) + eta = [[]] + for i in range(len(vec)): + eta[0].append(abs(vec[i])) + + for v in range(1, n_qubits+1): + eta.append([]) + for i in range(int(2**(n_qubits-v))): + eta[v].append(np.sqrt(eta[v-1][2*i]**2 + eta[v-1][2*i+1]**2)) + + omega_0 = [] + for i in range(len(vec)): + omega_0.append(np.angle(vec[i])) + + omega = [[]] + for i in range(len(vec)): + omega[0].append(2*np.angle(vec[i])) + + for v in range(1, n_qubits+1): + omega.append([]) + for i in range(2**(n_qubits-v)): + omega[v].append(0.) + for j in range(2**v): + omega[v][i] += omega_0[i*2**v+j]/2**(v-1) + + alphas = {} + for v in range(n_qubits, 0, -1): + for i in range(2**(n_qubits-v)): + if eta[v][i] < 1e-6: + alphas[f'alpha_{v}{i}'] = 0 + else: + alphas[f'alpha_{v}{i}'] = 2*np.arcsin(eta[v-1][2*i+1]/eta[v][i]) + + lambs = {} + for v in range(n_qubits, 0, -1): + for i in range(2**(n_qubits-v)): + lambs[f'lambda_{v}{i}'] = omega[v-1][2*i+1] - omega[v][i] + + return amp_circuit(n_qubits), ParameterResolver({**alphas, **lambs}) diff --git a/tests/st/test_algorithm/test_library/test_amplitude_encoder.py b/tests/st/test_algorithm/test_library/test_amplitude_encoder.py index b07a4203deefaa6ab0ac28136d3bb36b9fede7e2..bc7004728654d5783ee727cc33b6ea5dd08651fd 100644 --- a/tests/st/test_algorithm/test_library/test_amplitude_encoder.py +++ b/tests/st/test_algorithm/test_library/test_amplitude_encoder.py @@ -15,6 +15,7 @@ '''test for amplitude encoder''' import warnings +import numpy as np import pytest @@ -72,17 +73,29 @@ def test_amplitude_encoder(config): assert abs(state[3].real - 0.5) < 1e-6 assert abs(state[4].real - 0.5) < 1e-6 assert abs(state[5].real - 0.5) < 1e-6 - circuit, params = amplitude_encoder([0.5, -0.5, 0.5, 0.5], 3) + circuit, params = amplitude_encoder([0.5, 0.5, -0.5, -0.5], 3) sim.reset() sim.apply_circuit(circuit, params) - state = sim.get_qs(False) + state_0 = sim.get_qs(False) + state_1 = state_0 * np.conj(state_0[0]) + state = state_1/np.linalg.norm(state_1, ord=2) if backend == "mqmatrix": - assert abs(state[0][0].real - 0.25) < 1e-6 - assert abs(state[1][1].real - 0.25) < 1e-6 - assert abs(state[2][2].real - 0.25) < 1e-6 - assert abs(state[3][3].real - 0.25) < 1e-6 + pass else: assert abs(state[0].real - 0.5) < 1e-6 - assert abs(state[1].real + 0.5) < 1e-6 - assert abs(state[2].real - 0.5) < 1e-6 - assert abs(state[3].real - 0.5) < 1e-6 + assert abs(state[1].real - 0.5) < 1e-6 + assert abs(state[2].real + 0.5) < 1e-6 + assert abs(state[3].real + 0.5) < 1e-6 + circuit, params = amplitude_encoder([0.5j, 0.5j, -0.5j, -0.5j], 3) + sim.reset() + sim.apply_circuit(circuit, params) + state_0 = sim.get_qs(False) + state_1 = state_0 * np.conj(state_0[0]) + state = state_1/np.linalg.norm(state_1, ord=2) + if backend == "mqmatrix": + pass + else: + assert abs(state[0].real - 0.5) < 1e-6 + assert abs(state[1].real - 0.5) < 1e-6 + assert abs(state[2].real + 0.5) < 1e-6 + assert abs(state[3].real + 0.5) < 1e-6