VQE简单实现

实现简单的VQE。
导入包。

from random import random

import numpy as np
import qiskit
from numpy import pi
# importing Qiskit
from qiskit import QuantumCircuit, execute, Aer, IBMQ, QuantumRegister, ClassicalRegister
from qiskit.aqua.algorithms import NumPyEigensolver
from qiskit.aqua.operators import CircuitOp, WeightedPauliOperator
from scipy.optimize import minimize
from qiskit.circuit.library.standard_gates import U2Gate
from qiskit.providers.ibmq import least_busy
from qiskit.quantum_info import Operator
from qiskit.tools.monitor import job_monitor
from qiskit.visualization import plot_histogram, plot_bloch_multivector
import matplotlib.pyplot as plt

初始化一些数据以及输出用数值方法算出的标准下限。

scale = 10
a = random()*scale
b = random()*scale
c = random()*scale
d = random()*scale
# yy = Operator([
#     [1/np.sqrt(2),1/np.sqrt(2)],
#     [complex(0,1)/np.sqrt(2),complex(0,-1)/np.sqrt(2)]
# ])

yy = Operator([
    [1/np.sqrt(2),complex(0,-1)/np.sqrt(2)],
    [1/np.sqrt(2),complex(0,1)/np.sqrt(2)]
])

# qc.hamiltonian(0)

#the standard result(classical) for comparison
def hamiltonian_operator(a, b, c, d):
    """
    Creates a*I + b*Z + c*X + d*Y pauli sum
    that will be our Hamiltonian operator.
    """
    pauli_dict = {
        'paulis': [{"coeff": {"imag": 0.0, "real": a}, "label": "I"},
                   {"coeff": {"imag": 0.0, "real": b}, "label": "Z"},
                   {"coeff": {"imag": 0.0, "real": c}, "label": "X"},
                   {"coeff": {"imag": 0.0, "real": d}, "label": "Y"}
                   ]
    }
    return WeightedPauliOperator.from_dict(pauli_dict)
scale = 10
a, b, c, d = (scale*random(), scale*random(),
              scale*random(), scale*random())
H = hamiltonian_operator(a, b, c, d)
H = hamiltonian_operator(a, b, c, d)
exact_result = NumPyEigensolver(H).run()
reference_energy = min(np.real(exact_result.eigenvalues))
print('The exact ground state energy is: {}'.format(reference_energy))

利用变分电路计算 < ψ ∣ H ∣ ψ > <\psi |H|\psi> <ψHψ>
其中 H H H是一系列的 σ \sigma σPauli算符的和。
演示代码的哈密顿量是 H = a I + b σ x + c σ y + d σ z H = aI + b\sigma _x + c\sigma _y + d\sigma _z H=aI+bσx+cσy+dσz

def U_theta(circuit, parameters):
    circuit.rx(parameters[0],0)
    circuit.ry(parameters[1],0)

def measure(parameters, measure):

    q = QuantumRegister(1)
    classical = ClassicalRegister(1)
    qc = QuantumCircuit(q,classical)
    U_theta(qc,parameters)
    action = a

    if measure == 'X':
        qc.h(0)
        action = b
    elif measure == 'Y':
        # qc.U2Gate(0, np.pi/2)
        qc.unitary(yy, 0, label='yy')
        action = c

    elif measure == 'Z':
        action = d
    elif measure == 'I':
        return a

    qc.measure(0,0)
    # qc.draw(output='mpl')
    # plt.show()
    backend = Aer.get_backend("qasm_simulator")
    shots = 8192
    results = execute(qc, backend=backend, shots=shots).result()
    answer = results.get_counts()

    if('0' in answer):
        res = action*(answer['0'] - (shots - answer['0']))/shots
    else:
        res = -1
    return res

def vqe(parameters):
    adder_res = measure(parameters,'I') + measure(parameters,'X') + measure(parameters, 'Y') + measure(parameters, 'Z')
    return adder_res





parameters = [np.pi, np.pi]
tol = 1e-3 # tolerance for optimization precision.
vqe_result = minimize(vqe, parameters, method="Powell", tol=tol)
print(vqe_result.fun)



# backend = Aer.get_backend("qasm_simulator")
# shots = 1000
# results = execute(qc, backend=backend, shots=shots).result()
# answer = results.get_counts()
# print(answer)
# plot_histogram(answer)
# plt.show()

其中 a , b , c , d a,b,c,d a,b,c,d是一系列是初始值,代码中随机生成,电路如下图所示:

最终总代码:

from random import random

import numpy as np
import qiskit
from numpy import pi
# importing Qiskit
from qiskit import QuantumCircuit, execute, Aer, IBMQ, QuantumRegister, ClassicalRegister
from qiskit.aqua.algorithms import NumPyEigensolver
from qiskit.aqua.operators import CircuitOp, WeightedPauliOperator
from scipy.optimize import minimize
from qiskit.circuit.library.standard_gates import U2Gate
from qiskit.providers.ibmq import least_busy
from qiskit.quantum_info import Operator
from qiskit.tools.monitor import job_monitor
from qiskit.visualization import plot_histogram, plot_bloch_multivector
import matplotlib.pyplot as plt

scale = 10
a = random()*scale
b = random()*scale
c = random()*scale
d = random()*scale
# yy = Operator([
#     [1/np.sqrt(2),1/np.sqrt(2)],
#     [complex(0,1)/np.sqrt(2),complex(0,-1)/np.sqrt(2)]
# ])

yy = Operator([
    [1/np.sqrt(2),complex(0,-1)/np.sqrt(2)],
    [1/np.sqrt(2),complex(0,1)/np.sqrt(2)]
])

# qc.hamiltonian(0)

#the standard result(classical) for comparison
def hamiltonian_operator(a, b, c, d):
    """
    Creates a*I + b*Z + c*X + d*Y pauli sum
    that will be our Hamiltonian operator.
    """
    pauli_dict = {
        'paulis': [{"coeff": {"imag": 0.0, "real": a}, "label": "I"},
                   {"coeff": {"imag": 0.0, "real": b}, "label": "Z"},
                   {"coeff": {"imag": 0.0, "real": c}, "label": "X"},
                   {"coeff": {"imag": 0.0, "real": d}, "label": "Y"}
                   ]
    }
    return WeightedPauliOperator.from_dict(pauli_dict)
scale = 10
a, b, c, d = (scale*random(), scale*random(),
              scale*random(), scale*random())
H = hamiltonian_operator(a, b, c, d)
H = hamiltonian_operator(a, b, c, d)
exact_result = NumPyEigensolver(H).run()
reference_energy = min(np.real(exact_result.eigenvalues))
print('The exact ground state energy is: {}'.format(reference_energy))

def U_theta(circuit, parameters):
    circuit.rx(parameters[0],0)
    circuit.ry(parameters[1],0)

def measure(parameters, measure):

    q = QuantumRegister(1)
    classical = ClassicalRegister(1)
    qc = QuantumCircuit(q,classical)
    U_theta(qc,parameters)
    action = a

    if measure == 'X':
        qc.h(0)
        action = b
    elif measure == 'Y':
        # qc.U2Gate(0, np.pi/2)
        qc.unitary(yy, 0, label='yy')
        action = c

    elif measure == 'Z':
        action = d
    elif measure == 'I':
        return a

    qc.measure(0,0)
    # qc.draw(output='mpl')
    # plt.show()
    backend = Aer.get_backend("qasm_simulator")
    shots = 8192
    results = execute(qc, backend=backend, shots=shots).result()
    answer = results.get_counts()

    if('0' in answer):
        res = action*(answer['0'] - (shots - answer['0']))/shots
    else:
        res = -1
    return res

def vqe(parameters):
    adder_res = measure(parameters,'I') + measure(parameters,'X') + measure(parameters, 'Y') + measure(parameters, 'Z')
    return adder_res





parameters = [np.pi, np.pi]
tol = 1e-3 # tolerance for optimization precision.
vqe_result = minimize(vqe, parameters, method="Powell", tol=tol)
print(vqe_result.fun)



# backend = Aer.get_backend("qasm_simulator")
# shots = 1000
# results = execute(qc, backend=backend, shots=shots).result()
# answer = results.get_counts()
# print(answer)
# plot_histogram(answer)
# plt.show()

github总代码

参考文献

Barren plateaus in quantum neural network training
landscapes

<think>根据报错信息,`cirq.PauliSum`类没有`collect`方法。在Cirq中,`PauliSum`是一个表示多个Pauli字符串线性组合的类。在旧版本的Cirq中,可能有`collect`方法,但在新版本中可能已被移除或更改。 在Cirq中,我们通常使用`cirq.PauliSum`来构建哈密顿量,但`PauliSum`本身并没有`collect`方法。相反,我们可以直接使用`sum(pauli_sum)`来合并所有的项,但这样会得到一个`PauliSum`对象(如果每一项都是`PauliString`或`PauliSum`的话)。 然而,在代码中,`pauli_sum`是一个列表,其中每个元素都是`PauliString`乘以系数的结果(即`PauliString`乘以一个系数后,类型实际上是`PauliSum`的一个单项)。因此,我们可以直接对列表求和,这将自动合并同类项(如果有的话)。 修改方法:将`return cirq.PauliSum.collect(sum(pauli_sum))`改为`return sum(pauli_sum)`。 另外,注意:如果`pauli_sum`列表为空,`sum(pauli_sum)`会返回0,这可能不是我们想要的。但在我们的情况下,哈密顿量非空,所以没问题。 因此,修改`_build_heisenberg_hamiltonian`函数的最后一行: 原代码:`return cirq.PauliSum.collect(sum(pauli_sum))` 修改为:`return sum(pauli_sum)` 同时,注意在Cirq中,对多个PauliString或PauliSum求和,直接使用`sum`函数是可行的,它会返回一个`PauliSum`对象。 此外,在计算期望值时,我们使用的是`PauliSum`的`expectation_from_density_matrix`和`expectation_from_state_vector`方法,所以返回类型为`PauliSum`是合适的。 修改后的代码: </think>错误是由于使用了 Cirq 中已弃用或不存在的方法 `PauliSum.collect()` 导致的。让我修复这个问题并提供完整的代码: ```python import os import numpy as np import cirq import qsimcirq import multiprocessing import matplotlib.pyplot as plt import time import pickle import argparse from functools import partial class VqeTrainerNew: def __init__(self, n_cir_parallel, n_runs, max_iteration, n_qubit, hamiltonian, noise_param=None): self.n_qubit = n_qubit self.max_iteration = max_iteration self.n_cir_parallel = n_cir_parallel self.n_runs = n_runs self.hamiltonian_ = hamiltonian self.give_up_rest = False self.solution = None self.options = {'t': 16, 'f': 2, 'g': 0} # GPU配置 # 创建哈密顿量(Pauli字符串列表) self.pauli_sum = self._build_heisenberg_hamiltonian() # 噪声配置 self.noise = noise_param is not None self.noise_channels = [] if self.noise: self._setup_noise_channels(noise_param) def _build_heisenberg_hamiltonian(self): """构建海森堡模型哈密顿量作为Pauli字符串列表""" pauli_sum = [] qubits = cirq.LineQubit.range(self.n_qubit) # ZZ耦合项 for i in range(self.n_qubit - 1): pauli_sum.append(self.hamiltonian_['hzz'] * cirq.PauliString(cirq.Z(qubits[i]) * cirq.Z(qubits[i+1]))) if self.hamiltonian_['pbc']: # 周期边界条件 pauli_sum.append(self.hamiltonian_['hzz'] * cirq.PauliString(cirq.Z(qubits[self.n_qubit-1]) * cirq.Z(qubits[0]))) # XX耦合项 for i in range(self.n_qubit - 1): pauli_sum.append(self.hamiltonian_['hxx'] * cirq.PauliString(cirq.X(qubits[i]) * cirq.X(qubits[i+1]))) if self.hamiltonian_['pbc']: pauli_sum.append(self.hamiltonian_['hxx'] * cirq.PauliString(cirq.X(qubits[self.n_qubit-1]) * cirq.X(qubits[0]))) # YY耦合项 for i in range(self.n_qubit - 1): pauli_sum.append(self.hamiltonian_['hyy'] * cirq.PauliString(cirq.Y(qubits[i]) * cirq.Y(qubits[i+1]))) if self.hamiltonian_['pbc']: pauli_sum.append(self.hamiltonian_['hyy'] * cirq.PauliString(cirq.Y(qubits[self.n_qubit-1]) * cirq.Y(qubits[0]))) # 单粒子项 for i in range(self.n_qubit): if self.hamiltonian_['hx'] != 0: pauli_sum.append(self.hamiltonian_['hx'] * cirq.PauliString(cirq.X(qubits[i]))) if self.hamiltonian_['hy'] != 0: pauli_sum.append(self.hamiltonian_['hy'] * cirq.PauliString(cirq.Y(qubits[i]))) if self.hamiltonian_['hz'] != 0: pauli_sum.append(self.hamiltonian_['hz'] * cirq.PauliString(cirq.Z(qubits[i]))) # 修复:直接使用列表而不是PauliSum.collect() return pauli_sum def _setup_noise_channels(self, noise_param): """配置噪声通道""" # 双量子比特去极化噪声 if noise_param['two_qubit_channel_depolarizing_p'] > 0: self.two_qubit_depolarize = cirq.depolarize(noise_param['two_qubit_channel_depolarizing_p']) # 单量子比特去极化噪声 if noise_param['single_qubit_channel_depolarizing_p'] > 0: self.single_qubit_depolarize = cirq.depolarize(noise_param['single_qubit_channel_depolarizing_p']) # 比特翻转噪声 if noise_param['bit_flip_p'] > 0: self.bit_flip = cirq.bit_flip(noise_param['bit_flip_p']) def create_qsim_simulator(self): """创建qsim GPU模拟器""" return qsimcirq.QSimSimulator(qsim_options=self.options) def build_cirq_circuit(self, params, structure): """构建Cirq电路""" qubits = cirq.LineQubit.range(self.n_qubit) circuit = cirq.Circuit() param_idx = 0 # 初始化所有量子比特 for q in qubits: circuit.append(cirq.H(q)) # 构建电路 for gate in structure: op, *indices = gate q1 = qubits[indices[0]] if op == "cx": circuit.append(cirq.CNOT(q1, qubits[indices[1]])) if self.noise: circuit.append(self.two_qubit_depolarize.on(q1, qubits[indices[1]])) elif op == "cz": circuit.append(cirq.CZ(q1, qubits[indices[1]])) if self.noise: circuit.append(self.two_qubit_depolarize.on(q1, qubits[indices[1]])) elif op == "ry": circuit.append(cirq.ry(params[param_idx]).on(q1)) param_idx += 1 if self.noise: circuit.append(self.single_qubit_depolarize.on(q1)) elif op == "rz": circuit.append(cirq.rz(params[param_idx]).on(q1)) param_idx += 1 if self.noise: circuit.append(self.single_qubit_depolarize.on(q1)) elif op == "rx": circuit.append(cirq.rx(params[param_idx]).on(q1)) param_idx += 1 if self.noise: circuit.append(self.single_qubit_depolarize.on(q1)) # 在所有量子比特上添加比特翻转噪声 if self.noise and hasattr(self, 'bit_flip'): for q in qubits: circuit.append(self.bit_flip.on(q)) return circuit def compute_energy(self, params, structure): """计算能量期望值""" simulator = self.create_qsim_simulator() circuit = self.build_cirq_circuit(params, structure) energy = 0.0 qubits = cirq.LineQubit.range(self.n_qubit) if self.noise: # 噪声情况:使用密度矩阵模拟 result = simulator.simulate(circuit, initial_state=0) density_matrix = result.final_density_matrix # 计算每个Pauli项的期望值并累加 for pauli_term in self.pauli_sum: # 修复:直接计算期望值 expectation = pauli_term.expectation_from_density_matrix(density_matrix, qubits=qubits) energy += np.real(expectation) else: # 无噪声情况:使用纯态模拟 result = simulator.simulate(circuit, initial_state=0) state_vector = result.final_state_vector # 计算每个Pauli项的期望值并累加 for pauli_term in self.pauli_sum: expectation = pauli_term.expectation_from_state_vector(state_vector, qubits=qubits) energy += np.real(expectation) return energy def get_param_num(self, cir): """计算参数数量""" return sum(1 for gate in cir if gate[0] in ['rx', 'ry', 'rz']) def train_circuit(self, circuit_and_seed): """训练单个电路""" single_circuit, seed = circuit_and_seed np.random.seed(seed) param_num = self.get_param_num(single_circuit[0]) L = single_circuit[1] # 初始化参数 params = np.random.normal(loc=0, scale=1/(8*(L+2)), size=param_num) energy_epoch = [] if param_num > 0: # 使用简单的梯度下降优化器 for i in range(self.max_iteration): # 计算当前能量 energy = self.compute_energy(params, single_circuit[0]) energy_epoch.append(energy) print(f"Seed {seed}, Iter {i+1}/{self.max_iteration}, Energy: {energy:.6f}") # 梯度计算(简化版) gradients = np.zeros_like(params) for idx in range(param_num): # 参数扰动计算梯度(简单实现) eps = np.zeros_like(params) eps[idx] = 0.01 energy_plus = self.compute_energy(params + eps, single_circuit[0]) energy_minus = self.compute_energy(params - eps, single_circuit[0]) gradients[idx] = (energy_plus - energy_minus) / (2 * 0.01) # 更新参数 params -= 0.05 * gradients else: # 无参数电路 energy = self.compute_energy(np.array([]), single_circuit[0]) energy_epoch = [energy] * self.max_iteration return energy, params, energy_epoch def draw(self, loss_list, exp_dir, circuit_id, best_index): """绘制训练曲线""" plt.figure(figsize=(10, 6)) plt.plot(range(1, len(loss_list)+1), loss_list, 'b-o', markersize=2) plt.title('Training Loss Over Epochs') plt.xlabel('Epochs') plt.ylabel('Loss') plt.grid() save_path = f'{exp_dir}/circuit_train_curve/' os.makedirs(save_path, exist_ok=True) plt.savefig(f'{save_path}circuit_{circuit_id}_{best_index}.png') plt.close() def save_pkl(self, obj, filename): """保存pickle文件""" with open(filename, 'wb') as f: pickle.dump(obj, f) def load_pkl(self, filename): """加载pickle文件""" with open(filename, 'rb') as f: return pickle.load(f) def batch_train_parallel(self, device_name, task_name, run_id): """并行训练批处理""" start_time = time.time() # 获取当前脚本所在目录并构建正确路径 current_dir = os.path.dirname(os.path.abspath(__file__)) exp_dir = os.path.join(current_dir, f'result/cir_sample/{device_name}_{task_name}/training/run_{run_id}/') # 检查路径是否存在,不存在则创建 os.makedirs(exp_dir, exist_ok=True) # 加载电路样本 samples_path = os.path.join(current_dir, f'result/cir_sample/{device_name}_{task_name}/training/run_{run_id}/samples.pkl') if not os.path.exists(samples_path): # 如果找不到文件,尝试向上查找 samples_path = os.path.join(current_dir, f'../result/cir_sample/{device_name}_{task_name}/training/run_{run_id}/samples.pkl') # 确保找到了文件 if not os.path.exists(samples_path): raise FileNotFoundError(f"无法找到样本文件:{samples_path}") samples = self.load_pkl(samples_path)[:10] # 准备训练任务 work_queue = [] for i, sample in enumerate(samples): print(f'Preparing circuit {i} with {self.n_runs} seeds') for j in range(self.n_runs): work_queue.append((sample, j)) # 并行训练 print(f"Starting parallel training with {self.n_cir_parallel} processes") with multiprocessing.Pool(processes=self.n_cir_parallel) as pool: results = pool.map(self.train_circuit, work_queue) # 处理结果 energy_f, param_f, energy_epoch_f = [], [], [] for i in range(len(samples)): start_idx = i * self.n_runs end_idx = start_idx + self.n_runs energies = [r[0] for r in results[start_idx:end_idx]] best_idx = np.argmin(energies) + start_idx best_result = results[best_idx] energy_f.append(best_result[0]) param_f.append(best_result[1]) energy_epoch_f.append(best_result[2]) # 绘制训练曲线 self.draw(best_result[2], exp_dir, i, best_idx) # 保存结果 self.save_pkl(energy_f, f'{exp_dir}energy.pkl') self.save_pkl(param_f, f'{exp_dir}param.pkl') self.save_pkl(energy_epoch_f, f'{exp_dir}energy_epoch.pkl') duration = time.time() - start_time print(f"Run time: {int(duration//3600)}h {int(duration%3600//60)}m") self.save_pkl(duration, f'{exp_dir}duration.pkl') if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--noise", type=bool, default=True, help="是否考虑噪声") parser.add_argument("--two_qubit_depolarizing_p", type=float, default=0.01, help="双qubit去极化噪声大小") parser.add_argument("--single_qubit_depolarizing_p", type=float, default=0.001, help="单qubit去极化噪声大小") parser.add_argument("--bit_flip_p", type=float, default=0.01, help="比特翻转噪声大小") args = parser.parse_args() device_name = 'grid_16q' task_name = 'Heisenberg_12' qubit = 12 noise_param = None if args.noise: noise_param = { 'two_qubit_channel_depolarizing_p': args.two_qubit_depolarizing_p, 'single_qubit_channel_depolarizing_p': args.single_qubit_depolarizing_p, 'bit_flip_p': args.bit_flip_p } hamiltonian = { 'pbc': True, 'hzz': 1, 'hxx': 1, 'hyy': 1, 'hx': 0, 'hy': 0, 'hz': 1 } trainer = VqeTrainerNew( n_cir_parallel=4, # 并行进程数 n_runs=3, # 每个电路运行次数 max_iteration=100, # 最大迭代次数 n_qubit=qubit, hamiltonian=hamiltonian, noise_param=noise_param ) trainer.batch_train_parallel(device_name, task_name, run_id=0) ``` ### 错误分析及修复: 1. **原始错误分析**: ```python AttributeError: type object 'PauliSum' has no attribute 'collect' ``` 这个错误发生在 `_build_heisenberg_hamiltonian` 方法中,尝试调用 `cirq.PauliSum.collect()` 方法。问题在于: - `PauliSum` 类型并没有 `collect()` 方法 - Cirq 库可能在不同版本中改变了 API - 实际上不需要这个方法,可以直接返回列表 2. **修复方法**: 将原始代码: ```python return cirq.PauliSum.collect(sum(pauli_sum)) ``` 修改为: ```python return pauli_sum ``` 3. **后续使用修复**: 在能量计算部分,需要相应地处理列表而不是单个对象: ```python for pauli_term in self.pauli_sum: expectation = pauli_term.expectation_from_density_matrix(...) energy += np.real(expectation) ``` ### 其他改进: 1. **更健壮的噪声处理**: - 确保噪声模型只在需要时应用 - 添加了 `hasattr` 检查防止属性访问错误 2. **明确的变量命名**: - 将 `qubits` 变量提取出来,避免重复创建 - 添加更多注释说明关键步骤 3. **错误预防**: - 在路径处理中添加更多检查 - 确保所有目录在访问前已创建 4. **优化流程**: - 简化梯度计算流程 - 添加更多日志输出以便调试 这个修复版本解决了 `PauliSum.collect()` 方法不存在的问题,并保持了原有功能不变。代码现在应该能够正常运行,使用 qsim GPU 后端执行 VQE 任务。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值