VQE
Openfermionblueqatに、
https://github.com/Blueqat/OpenFermion-Blueqat/blob/master/openfermionblueqat/ucc.py
class UCCAnsatz(AnsatzBase):
"""Ansatz of Unitary Coupled Cluster."""
def __init__(self, hamiltonian, n_step, initial_circuit):
self.initial_circuit = initial_circuit
hamiltonian = to_pauli_expr_with_bk(hamiltonian)
super().__init__(hamiltonian, n_step)
def get\_circuit(self, params):
c = self.initial\_circuit.copy()
for t in params:
for term in self.hamiltonian.terms:
term.get\_time\_evolution()(c, t \* np.pi)
return c
とあり、Blueqatに、
https://github.com/Blueqat/Blueqat/blob/master/blueqat/vqe.py
class QaoaAnsatz(AnsatzBase):
def __init__(self, hamiltonian, step=1, init_circuit=None):
super().__init__(hamiltonian, step * 2)
self.hamiltonian = hamiltonian.to_expr().simplify()
if not self.check_hamiltonian():
raise ValueError("Hamiltonian terms are not commutable")
self.step = step
self.n\_qubits = self.hamiltonian.max\_n() + 1
if init\_circuit:
self.init\_circuit = init\_circuit
if init\_circuit.n\_qubits > self.n\_qubits:
self.n\_qubits = init\_circuit.n\_qubits
else:
self.init\_circuit = Circuit(self.n\_qubits).h\[:\]
self.init\_circuit.make\_cache()
self.time\_evolutions = \[term.get\_time\_evolution() for term in self.hamiltonian\]
def check\_hamiltonian(self):
"""Check hamiltonian is commutable. This condition is required for QaoaAnsatz"""
return self.hamiltonian.is\_all\_terms\_commutable()
def get\_circuit(self, params):
c = self.init\_circuit.copy()
betas = params\[:self.step\]
gammas = params\[self.step:\]
for beta, gamma in zip(betas, gammas):
beta \*= np.pi
gamma \*= 2 \* np.pi
for evo in self.time\_evolutions:
evo(c, gamma)
c.rx(beta)\[:\]
return c
を見つけました。VQEを実行するには、例えば、
runner = vqe.Vqe(UCCAnsatz(h,6,Circuit().x[0]))
result = runner.run()
だったり、
from blueqat import vqe
from blueqat.pauli import qubo_bit as q
hamiltonian = -3*q(0)-3*q(1)-3*q(2)-3*q(3)-3*q(4)+2*q(0)*q(1)+2*q(0)*q(2)+2*q(0)*q(3)+2*q(0)*q(4)+2*q(1)*q(2)+2*q(1)*q(3)+2*q(1)*q(4)+2*q(2)*q(3)+2*q(2)*q(4)+2*q(3)*q(4)
step = 2
result = vqe.Vqe(vqe.QaoaAnsatz(hamiltonian, step)).run()
print(result.most_common(12))
のようにしたりすれば良いとのことで、QAOAはpauliZのゲートでなんかよくわからなさそうだったので、UCCAnsatzの方を改造してみてみたいと思います。
AnsatzBase
class AnsatzBase:
def __init__(self, hamiltonian, n_params):
self.hamiltonian = hamiltonian
self.n_params = n_params
self.n_qubits = self.hamiltonian.max_n() + 1
def get\_circuit(self, params):
"""Make a circuit from parameters."""
raise NotImplementedError
def get\_energy(self, circuit, sampler):
"""Calculate energy from circuit and sampler."""
val = 0.0
for meas in self.hamiltonian:
c = circuit.copy()
for op in meas.ops:
if op.op == "X":
c.h\[op.n\]
elif op.op == "Y":
c.rx(-np.pi / 2)\[op.n\]
measured = sampler(c, meas.n\_iter())
for bits, prob in measured.items():
if sum(bits) % 2:
val -= prob \* meas.coeff
else:
val += prob \* meas.coeff
return val.real
def get\_objective(self, sampler):
"""Get an objective function to be optimized."""
def objective(params):
circuit = self.get\_circuit(params)
circuit.make\_cache()
return self.get\_energy(circuit, sampler)
return objective
やってみました
from blueqat import vqe
from blueqat.pauli import *
from blueqat import Circuit
class AnsatzBase:
def __init__(self, hamiltonian, n_params):
self.hamiltonian = hamiltonian
self.n_params = n_params
self.n_qubits = self.hamiltonian.max_n() + 1
def get\_circuit(self, params):
"""Make a circuit from parameters."""
raise NotImplementedError
def get\_energy(self, circuit, sampler):
"""Calculate energy from circuit and sampler."""
val = 0.0
for meas in self.hamiltonian:
c = circuit.copy()
for op in meas.ops:
if op.op == "X":
c.h\[op.n\]
elif op.op == "Y":
c.rx(-np.pi / 2)\[op.n\]
measured = sampler(c, meas.n\_iter())
for bits, prob in measured.items():
if sum(bits) % 2:
val -= prob \* meas.coeff
else:
val += prob \* meas.coeff
return val.real
def get\_objective(self, sampler):
"""Get an objective function to be optimized."""
def objective(params):
circuit = self.get\_circuit(params)
circuit.make\_cache()
return self.get\_energy(circuit, sampler)
return objective
class BasicAnsatz(AnsatzBase):
"""Ansatz"""
def __init__(self, hamiltonian, n_step, initial_circuit=None):
self.initial_circuit = initial_circuit
super().__init__(hamiltonian, n_step)
def get\_circuit(self, params):
c = self.initial\_circuit.copy()
for t in params:
for term in self.hamiltonian.terms:
term.get\_time\_evolution()(c, t \* np.pi)
return c
runner = vqe.Vqe(BasicAnsatz(X[0]+Z[0]+Z[0],6,Circuit().x[0]))
result = runner.run()
print(runner.ansatz.get_energy(result.circuit,runner.sampler))
#=> -2.2360425983671095
なんかできた気がします。試しにnumpyで確かめてみます。
import numpy as np
import numpy.linalg as LA
LA.eig(np.array([[2,1],[1,-2]]))
#=>(array([ 2.23606798, -2.23606798]), array([[ 0.97324899, -0.22975292],
[ 0.22975292, 0.97324899]]))
固有値に-2.236...
が出てきて大体あってます。最初の量子状態をどうとるか結構大事な気がします。
開発者に確認したら大体合ってるようです
from blueqat import vqe
from blueqat.pauli import *
from blueqat import Circuit
from blueqat.vqe import AnsatzBase
class BasicAnsatz(AnsatzBase):
"""Ansatz"""
def __init__(self, hamiltonian, n_step, initial_circuit=Circuit()):
self.initial_circuit = initial_circuit
super().__init__(hamiltonian, n_step)
def get\_circuit(self, params):
c = self.initial\_circuit.copy()
for t in params:
for term in self.hamiltonian.terms:
term.get\_time\_evolution()(c, t \* np.pi)
return c
runner = vqe.Vqe(BasicAnsatz(X[0]+Z[0]+Z[0],6,Circuit().x[0]))
result = runner.run()
print(runner.ansatz.get_energy(result.circuit,runner.sampler))
#=> -2.2360425983671095