Hi, I hope you are doing well. I am trying to run a natural gradient descent optimization on my circuit. My original work was in qiskit but I switched to pennylane for its qng optimizer. I noticed that using similar routine for making ansatz in pennylane turns out to be different than qiskit’s ansatz so I am now making my ansatz in qiskit and converting it to qml’s ansatz and running the optimization. The ansatz conversion runs smoothely but I am having errors when I put it in qngoptimizer’s step_and_cost function. I was originally having an error where the parameters I was binding to my qiskit ansatz were tensor or autograd.arraybox so I tried converting them into a float to bind them to my qiskit ansatz. Now, I am getting an error of “Value error: need at least one array to stack.” Can you kindly help me with this error or guide me how I can use my qiskit ansatz with pennylane’s qng optimizer?
I have the following code:
from qiskit import QuantumCircuit, QuantumRegister
from qiskit.circuit import Parameter
import numpy as np
from pennylane import numpy as nnn
import pickle
import pennylane as qml
import pennylane_qiskit
from observable import returnStringObservable
dev = qml.device("default.qubit", wires=8)
connectivity = [(1, 2), (0, 6), (2, 3), (0, 7), (3, 4), (0, 1), (4, 5), (0, 2), (5, 6), (0, 3), (6, 7), (0, 4), (7, 1), (0, 5)]
boundary = [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 1)]
connectivity = list(map(tuple, connectivity))
boundary = list(map(tuple, boundary))
class TwoQubitUnitary(QuantumCircuit):
def __init__(self,):
super().__init__()
work = QuantumRegister(2, name='q')
self.add_register(work)
self.u(Parameter('p0'), Parameter('p1'), Parameter('p2'), work[0])
self.u(Parameter('p3'), Parameter('p4'), Parameter('p5'), work[1])
self.cx(work[1], work[0])
self.rz(Parameter('p6'), work[0])
self.ry(Parameter('p7'), work[1])
self.cx(work[0], work[1])
self.ry(Parameter('p8'), work[1])
self.cx(work[1], work[0])
self.u(Parameter('p9'), Parameter('p10'), Parameter('p11'), work[0])
self.u(Parameter('p12'), Parameter('p13'), Parameter('p14'), work[1])
num_qubits = 8
boundary_start = 1
conec_list = [list((i, j)) for (i, j) in connectivity]
paramsList = list()
energyList = list()
def qiskitToQml(parameters):
num_gates = len(parameters)/15
gates = list()
for i in range(int(num_gates)):
print(i)
relevant_params = parameters[i*15:(i*15)+15]
gate = TwoQubitUnitary()
if (type(relevant_params) == nnn.tensor):
gate = gate.bind_parameters({gate.parameters.data[index]: val.unwrap() for (index, val) in enumerate(relevant_params)})
else:
gate = gate.bind_parameters({gate.parameters.data[index]: (val._value).unwrap() for (index, val) in enumerate(relevant_params)})
# gate = gate.bind_parameters({gate.parameters.data[index]: val for (index, val) in enumerate(relevant_params)})
gates.append(gate)
ansatz = QuantumCircuit(num_qubits)
print("===start===")
for (index, (i, j)) in enumerate(connectivity):
if index % 2 == 0:
ansatz.append(gates[1], [i, j])
else:
ansatz.append(gates[0], [i, j])
for (index, (i, j)) in enumerate(connectivity[::-1]):
if index % 2 == 0:
ansatz.append(gates[3], [i, j])
else:
ansatz.append(gates[2], [i, j])
print("===End====")
return qml.from_qiskit(ansatz.decompose())
op_list = [(qml.pauli.utils.string_to_pauli_word(singleEntry)) for singleEntry in returnStringObservable(boundary_start, num_qubits)]
H = qml.Hamiltonian([-1]*len(op_list), op_list)
@qml.qnode(dev)
def cost_fn(params):
circ = qiskitToQml(params)
circ()
return qml.expval(H)
max_iterations = 1000
step_size = .01
conv_tol = 5e-07
opt = qml.QNGOptimizer(stepsize=step_size, approx='diag')
params = np.random.normal(-0.3, .1, 15*4)
qngd_param_history = [params]
qngd_cost_history = []
params, prev_energy = opt.step_and_cost(cost_fn, nnn.array(params, requires_grad=True))
print(prev_energy)
for n in range(max_iterations):
params, next_energy = opt.step_and_cost(cost_fn, nnn.array(params, requires_grad=True))
qngd_param_history.append(params)
qngd_cost_history.append(next_energy)
conv = np.abs(next_energy - prev_energy)
print(
"Iteration = {:}, Energy = {:.8f} Ha, Convergence parameter = {"
":.8f} Ha".format(n, next_energy, conv)
)
if conv <= conv_tol:
break
prev_energy = next_energy
pickle.dump([qngd_param_history, qngd_cost_history], open("qml_depth_1_8_15_params.pkl", "wb"))
print()
print("Final value of the energy = {:.8f} Ha".format(next_energy))
print("Number of iterations = ", n)