import tequila as tq
from tequila import numpy as np
from numpy import pi
import matplotlib.pyplot as plt
import matplotlib.pyplot as plt
def main(H,U, variables, variance_threshold=1.e-8, list=None):
'''This is the main part of this programm, where the optimization strategies "folded spectrum", "approximation" and "projection"
are being tested and plotted.
Input: Hamiltonian H
curcuit U
variables/angles as dictionary
variance threshold (optional)
list of ordered functions (optional) eg. ["A","P","F"] for Approximation, Projection, Folded Spectrum
Output: 1D or 2D graphics showing the optimization process as well as the Eigenvalues/Eigenstates of H'''
eigenvalues, vv = np.linalg.eigh(H.to_matrix())
oneDimensional = True
if (len(variables) == 2):
oneDimensional=False
variables = {"a": -1, "b": -0.5}
else:
variables = {"a": -1}
circuit_list = []
constant_list = []
list_length = 4
constant = 1
for l in range(list_length):
circuit_list.append(U.map_variables(variables))
constant_list.append(constant)
constant += 1
if list==None:
# First Projection, then Approximation, then Folded Spectrum
E = eigensolver.expectation_value_orthogonality_constraint(H,U, circuit_list, constant_list)
E_optimized = eigensolver.minimization(E, {"method":"BFGS"})
eigensolver.plotting_preparation(E, E_optimized, "Energy with orthogonality constraint", eigenvalues, oneDimensional)
if eigensolver.proof_eigenstate(H,U, variables, variance_threshold):
print("The optimal eigenstate with a variance <= ", variance_threshold, "was found.")
variables = E_optimized.variables
mu = tq.simulate(tq.ExpectationValue(H=H, U=U), variables=variables)
E_AFS = eigensolver.expectation_value_approximation(H,U, mu)
E_AFS_optimized = eigensolver.minimization(E_AFS, {"method":"BFGS", "initial_values":variables})
eigensolver.plotting_preparation(E_AFS, E_AFS_optimized, "Energy with approximation", eigenvalues, oneDimensional)
if eigensolver.proof_eigenstate(H,U, variables, variance_threshold):
print("The optimal eigenstate with a variance <= ", variance_threshold, "was found.")
variables = E_AFS_optimized.variables
mu = tq.simulate(tq.ExpectationValue(H=H, U=U), variables=variables)
E_FS = eigensolver.expectation_value_folded_spectrum(H,U, mu)
E_FS_optimized = eigensolver.minimization(E_FS, {"method":"BFGS", "initial_values":variables})
eigensolver.plotting_preparation(E_FS, E_FS_optimized, "Energy with folded spectrum", eigenvalues, oneDimensional)
if eigensolver.proof_eigenstate(H,U, variables, variance_threshold):
print("The optimal eigenstate with a variance <= ", variance_threshold, "was found.")
else:
for l in list:
if (l.isalpha()==False or len(l)!= 1):
raise ValueError('An elemet of the list is not one letter')
if (l == list[0]):
mu = 1
else:
variables = E_optimized.variables
mu = tq.simulate(tq.ExpectationValue(H=H, U=U), variables=variables)
if (l == "A"):
E = eigensolver.expectation_value_approximation(H,U, mu)
if (l == list[0]):
E_optimized = eigensolver.minimization(E, {"method":"BFGS"})
else:
E_optimized = eigensolver.minimization(E, {"method":"BFGS", "initial_values":variables})
eigensolver.plotting_preparation(E, E_optimized, "Energy with approximation", eigenvalues, oneDimensional)
if eigensolver.proof_eigenstate(H,U, variables, variance_threshold):
print("The optimal eigenstate with a variance <= ", variance_threshold, "was found.")
elif (l == "P"):
E = eigensolver.expectation_value_orthogonality_constraint(H,U, circuit_list, constant_list)
if (l == list[0]):
E_optimized = eigensolver.minimization(E, {"method":"BFGS"})
else:
E_optimized = eigensolver.minimization(E, {"method":"BFGS", "initial_values":variables})
eigensolver.plotting_preparation(E, E_optimized, "Energy with orthogonality constraint", eigenvalues, oneDimensional)
if eigensolver.proof_eigenstate(H,U, variables, variance_threshold):
print("The optimal eigenstate with a variance <= ", variance_threshold, "was found.")
elif (l == "F"):
E = eigensolver.expectation_value_folded_spectrum(H,U, mu)
if (l == list[0]):
E_optimized = eigensolver.minimization(E, {"method":"BFGS"})
else:
E_optimized = eigensolver.minimization(E, {"method":"BFGS", "initial_values":variables})
eigensolver.plotting_preparation(E, E_optimized, "Energy with folded spectrum", eigenvalues, oneDimensional)
if eigensolver.proof_eigenstate(H,U, variables, variance_threshold):
print("The optimal eigenstate with a variance <= ", variance_threshold, "was found.")
else:
raise ValueError('An elemet of the list is not letter "A", "P" or "F"')
#----------------General functions------------
class eigensolver:
def expectation_value_folded_spectrum(H,U, constant):
return tq.ExpectationValue(H=(H-constant)**2, U=U)
def expectation_value_approximation(H,U, constant):
return (tq.ExpectationValue(H=H, U=U)-constant)**2
def expectation_value_orthogonality_constraint(H,U, circuit_list, constant_list):
E = tq.ExpectationValue(H=H, U=U)
if (len(circuit_list) != len(constant_list)):
raise ValueError(f"Circuit_list and constant_list have different lengths. len(circuit_list): '{len(circuit_list)}', len(constant_list): '{len(constant_list)}'")
list_length = len(circuit_list)
for l in range(list_length):
if (circuit_list[l].extract_variables() == None):
raise ValueError(f"Circuit_list contains unparametrized elements")
U_list = []
for i in range(0, list_length):
U_k = U + circuit_list[i].dagger()
P_k = 1
for k in U_k.qubits:
P_k*= tq.paulis.Qp(k)
E_k = tq.ExpectationValue(H=P_k, U=U_k)
U_list.append(constant_list[i]*E_k)
return E + sum(U_list)
def minimization(E, dict_of_parameters=None):
if dict_of_parameters is None:
dict_of_parameters = {}
if not isinstance(dict_of_parameters, dict):
raise TypeError(f"dict expected, got '{type(dict_of_parameters).__name__}'")
dict_of_parameters.setdefault("method", "BFGS")
dict_of_parameters.setdefault("initial_values", "random")
return tq.minimize(E, **dict_of_parameters)
def proof_eigenstate(H, U, variables, variance_threshold=1.e-4):
V = ((tq.ExpectationValue(H=H, U=U)) **2 - tq.ExpectationValue(H=H**2, U=U)).apply(abs)
V = tq.simulate(V, variables)
return V <= variance_threshold
def get_optimization_energies(result):
return result.history.energies_calls
def get_optimization_angles(result, eigenvalues):
mod = len(eigenvalues)
angle_dots = [{k:v % mod for k,v in x.items()} for x in result.history.angles_calls]
angles_np = np.array([list(i.values()) for i in angle_dots])
return angles_np
def compile_E_values1D(fE, angle_range):
return [fE({"a":v}) for v in angle_range]
def compile_E_values2D(fE, angle_range):
fE_result = []
for v in angle_range:
for w in angle_range:
fE_result.append(fE({"a":v, "b":w}))
return fE_result
def compile_dE_values1D(fdE, angle_range):
return [fdE({"a":v}) for v in angle_range]
def compile_dE_values2D(fdE, angle_range):
fE_result = []
for v in angle_range:
for w in angle_range:
fE_result.append(fdE({"a":v, "b":w}))
return fE_result
# calculating min and max values of range of all energies (E or dE) for plotting. Returning array [y_min, y_max]
def min_max_y_value(E, values_E, values_dE, energy_dots):
min_max = []
all_energy_values = []
for v in values_E:
all_energy_values.append(v)
for v in values_dE:
all_energy_values.append(v)
y_min = energy_dots[0]
y_max = energy_dots[0]
for energy in all_energy_values:
if energy < y_min:
y_min = energy
if energy > y_max:
y_max = energy
min_max.append(y_min)
min_max.append(y_max)
return min_max
def plotting1D(aprox_name, angle_range, angles_np, values_E, values_dE, fE, energy_np, start_dot_E, end_dot_E, start_dot_angle, end_dot_angle, angles_of_eigenvalues, y_min, y_max):
plt.plot(angle_range, values_E, label= str(aprox_name))
plt.plot(angle_range, values_dE, label= 'Derivation of the ' + str(aprox_name))
plt.legend([str(aprox_name), 'Derivation of the ' + str(aprox_name)])
plt.scatter(angles_np, energy_np)
for a in angles_of_eigenvalues:
plt.plot(a, fE({"a":a}), "o",mfc = '#4CAF50',ms = 10,mec = 'r')
plt.vlines(x=a, colors='purple', ymin=y_min-5, ymax=y_max+5, ls='--', lw=2, label='Eigenvalues')
plt.annotate('Starting point',
ha = 'center', va = 'bottom',
xytext = (start_dot_angle , start_dot_E - 2),
xy = (start_dot_angle, start_dot_E),
arrowprops = { 'facecolor' : 'black', 'shrink' : 0.5, 'width' : 0.5, 'headwidth' : 10})
plt.annotate('End point',
ha = 'center', va = 'bottom',
xytext = (end_dot_angle, end_dot_E + 2),
xy = (end_dot_angle, end_dot_E),
arrowprops = { 'facecolor' : 'black', 'shrink' : 0.5, 'width' : 0.5, 'headwidth' : 10})
plt.xlabel("Eigenvalues")
plt.ylabel("Eigenstates")
plt.show()
def plotting2D(aprox_name, start_dot_E, end_dot_E, start_dot_angle, end_dot_angle, fE, angles_of_eigenvalues):
X=np.linspace(0.0, 2.0*np.pi,25)
Y=X
Z = np.zeros([25,25])
for i,x in enumerate(X):
for j,y in enumerate(Y):
Z[i,j]= fE({"a":x, "b":y})
X, Y = np.meshgrid(X, Y)
ax = plt.figure().add_subplot(projection='3d')
ax.plot_surface(X, Y, Z, edgecolor='royalblue', lw=0.5, rstride=8, cstride=8,
alpha=0.3, shade=True)
for a in angles_of_eigenvalues:
for b in angles_of_eigenvalues:
if (a == angles_of_eigenvalues[0] and b == angles_of_eigenvalues[0]):
ax.plot(a,b, fE({"a":a, "b":b}), "o",mfc = '#4CAF50',ms = 10,mec = 'r',label="Possible Eigenvalues")
else:
ax.plot(a,b, fE({"a":a, "b":b}), "o",mfc = '#4CAF50',ms = 10,mec = 'r')
if (fE({"a":end_dot_angle[0], "b":end_dot_angle[1]}) == fE({"a":start_dot_angle[0], "b":start_dot_angle[1]})):
ax.scatter3D(start_dot_angle[0], start_dot_angle[1],fE({"a":start_dot_angle[0], "b":start_dot_angle[1]}),color='red', s=25, label="Starting point equals End point")
else:
ax.scatter3D(end_dot_angle[0], end_dot_angle[1],fE({"a":end_dot_angle[0], "b":end_dot_angle[1]}),color='black', s=25, label="Starting point")
ax.scatter3D(start_dot_angle[0], start_dot_angle[1],fE({"a":start_dot_angle[0], "b":start_dot_angle[1]}),color='red', s=25, label="End point")
ax.set_xlabel('Angle "a"')
ax.set_ylabel('Angle "b"')
ax.set_zlabel('Energy')
ax.legend()
ax.set_title(str(aprox_name))
plt.show()
def plotting2D_chemistry(aprox_name, fE):
X=np.linspace(0.0, 2.0*np.pi,25)
Y=X
Z = np.zeros([25,25])
for i,x in enumerate(X):
for j,y in enumerate(Y):
Z[i,j]= fE({"a":x, "b":y})
X, Y = np.meshgrid(X, Y)
ax = plt.figure().add_subplot(projection='3d')
ax.plot_surface(X, Y, Z, edgecolor='royalblue', lw=0.5, rstride=8, cstride=8,
alpha=0.3, shade=True)
ax.set_xlabel('Angle "a"')
ax.set_ylabel('Angle "b"')
ax.set_zlabel('Energy')
ax.legend()
ax.set_title(str(aprox_name))
plt.show()
def plotting_preparation(E, result, aprox_name, eigenvalues, oneDimensional=True):
angle_range = (np.linspace(0,4,100))
angles_of_eigenvalues = eigenvalues
fE = tq.compile(E)
if oneDimensional:
values_E = eigensolver.compile_E_values1D(fE, angle_range)
dE = tq.grad(E, "a")
else:
values_E = eigensolver.compile_E_values2D(fE, angle_range)
dE = tq.grad(E, "a", "b")
fdE = tq.compile(dE)
if oneDimensional:
values_dE = eigensolver.compile_dE_values1D(fdE, angle_range)
else:
values_dE = eigensolver.compile_dE_values2D(fdE, angle_range)
angles = eigensolver.get_optimization_angles(result, eigenvalues)
energy_dots = eigensolver.get_optimization_energies(result)
if oneDimensional:
angles_np = []
for xs in angles:
for x in xs:
angles_np.append(x)
else:
angles_np = angles
y_min = eigensolver.min_max_y_value(E, values_E, values_dE, energy_dots)[0]
y_max = eigensolver.min_max_y_value(E, values_E, values_dE, energy_dots)[1]
start_dot_E = energy_dots[0]
end_dot_E = energy_dots[len(energy_dots)-1]
start_dot_angle = angles_np[0]
end_dot_angle = angles_np[len(energy_dots)-1]
if oneDimensional:
eigensolver.plotting1D(aprox_name, angle_range, angles_np, values_E, values_dE, fE, energy_dots, start_dot_E, end_dot_E, start_dot_angle, end_dot_angle, angles_of_eigenvalues, y_min, y_max)
else:
eigensolver.plotting2D(aprox_name, start_dot_E, end_dot_E, start_dot_angle, end_dot_angle, fE, angles_of_eigenvalues)
def min_max_energy_and_angle(fE,values_E, angle_range):
min_E = min(np.array(values_E))
for a in angle_range:
if (fE({"a":a}) == min_E):
min_angle = a
break
max_E = max(np.array(values_E))
for a in angle_range:
if (fE({"a":a}) == max_E):
max_angle = a
break
return min_E, max_E, min_angle, max_angle
# Given Hamiltonian H
H = 1.5-0.5*(tq.paulis.Z(1)-tq.paulis.Z(0)+tq.paulis.Z(0)*tq.paulis.Z(1)+tq.paulis.X(1)-tq.paulis.Z(0)*tq.paulis.X(1))
# ------------1D model--------------
a = tq.Variable("a")
variables = {"a": -1}
U = tq.gates.Ry(angle=(a)*np.pi,target=0)
U+= tq.gates.CNOT(0,1)
U+= tq.gates.Ry(angle=(a/2)*np.pi, target=1)
main(H, U, variables, variance_threshold=1.e-8, list=["A", "F", "P", "F"])
# ------------2D model--------------
a = tq.Variable("a")
b = tq.Variable("b")
variables = {"a":1.0, "b":0.7}
U = tq.gates.Ry(angle=(a)*np.pi,target=0)
U+= tq.gates.CNOT(0,1)
U+= tq.gates.Ry(angle=(b)*np.pi, target=1)
main(H, U, variables, variance_threshold=1.e-8, list=["A", "F", "P", "F"])