How to train a circuit imported from qiskit?

I want to train the following circuit:

from qiskit.providers.aer.noise import NoiseModel
from qiskit import QuantumCircuit
from qiskit.test.mock import FakeLagos
import pennylane as qml
import pennylane.numpy as np
from sklearn.metrics import accuracy_score

backend = FakeLagos()
noise_model = NoiseModel.from_backend(backend)

def get_qc(state, params):
    if type(params) != type(weights):
        params = params._value

    qc = QuantumCircuit(2)
    qc.initialize(state, qc.qubits)

    for l in range(3):
        for i in range(2):
            qc.u(*params[l][i], i)
            qc.cx(0, 1)
    return qc

dev = qml.device(
        "qiskit.aer",
        wires=2,
        shots=1000,
        noise_model=noise_model,
        backend="aer_simulator_statevector",
        seed_simulator=1,
        seed_transpiler=1,
    )

@qml.qnode(dev)
def circuit(state, params):
    params = params.reshape([3, 2, 3])
    qc = get_qc(state, params)
    qml.from_qiskit(qc)
    return qml.expval(qml.PauliZ(0))

weights = np.random.uniform(-np.pi, np.pi, [3, 2, 3], requires_grad=True)

def all_cost(parameters, x, y):
    error = 0
    for xi, yi in zip(x, y):
        error = error + (yi - circuit(xi, parameters)) ** 2
    return error / len(x)


def all_predict(x, parameters):
    y = []
    for xi in x:
        y.append(np.sign(circuit(xi, parameters)))
    return np.array(y)


initial_states_train = [[(0.2422281054276611 - 0.06185098981363071j), (-0.4195513856218597 - 0.10712905685563347j),
                         (-0.4195513856218597 - 0.10712905685563348j), (0.7266843162829835 - 0.1855529694408921j)],
                        [(0.4844562108553227 - 0.12370197962726152j), (0.48445621085532237 + 0.12370197962726144j),
                         (0.48445621085532237 + 0.12370197962726144j), (0.48445621085532237 - 0.12370197962726144j)]]
y_train = [-1, -1]

opt = qml.AdamOptimizer(0.1)

for i in range(10):
    weights = opt.step(lambda params: all_cost(params, initial_states_train, y_train), weights.ravel())

weights = weights.reshape([3, 2, 3])

print("Scores after training:")
y_predict_train = all_predict(initial_states_train, weights)
print(
    "Training Data:",
    accuracy_score(y_pred=y_predict_train, y_true=y_train),
)

Here, I am building a circuit using Qiskit and importing it into the QNode using qml.from_qiskit. The reason for not making the circuit directly in PennyLane is that I want to use qc.initialize to initialize the system in a given state.

The above is a working example that you can copy and run.

If I run this, the model does not train and I get:

lib\site-packages\autograd\tracer.py:14: UserWarning: Output seems independent of input.
  warnings.warn("Output seems independent of input.")

I can run the circuit and optimization directly in Qiskit, where it does work. So the issue seems to be with the PennyLane code.

    if type(params) != type(weights):
        params = params._value

These lines in the get_qc function are required as without these I get an error:

raise CircuitError(f"Invalid param type {type(parameter)} for gate {self.name}.")
qiskit.circuit.exceptions.CircuitError: "Invalid param type <class 'autograd.numpy.numpy_boxes.ArrayBox'> for gate u."

Hi @ankit27kh, I’m finding trouble running your code the way you have it.

The following code runs though so maybe it can help you advance from there.

import pennylane as qml
from pennylane import numpy as np
from qiskit.circuit import QuantumCircuit, Parameter

qc = QuantumCircuit(2)
theta = Parameter('θ')

state = [1/2,1/2,1/2,1/2]

qc.initialize(state, qc.qubits)
qc.rx(theta, [0])
qc.cx(0, 1)

my_circuit = qml.load(qc, format='qiskit')

#dev = qml.device('default.qubit', wires=2)
dev = qml.device(
        "qiskit.aer",
        wires=2,
        shots=1000,
        noise_model=noise_model,
        backend="aer_simulator_statevector",
        seed_simulator=1,
        seed_transpiler=1,
    )

@qml.qnode(dev)
def circuit(x):
    my_circuit(params={theta: x},wires=(1, 0))
    return qml.expval(qml.PauliZ(0))

theta_train = np.array(0.4, requires_grad=True)

opt = qml.GradientDescentOptimizer()

for i in range(100):
    theta_train = opt.step(circuit, theta_train)
    if i % 10 == 0:
        print('Cost', circuit(theta_train))

Please let us know if you advance in solving this issue. I’m thinking that the problem may be that PennyLane can’t differentiate “initialize”. Functions need to be “pure” and not update global variables, otherwise you get these ArrayBox problems.

The other option is to use one of the PennyLane templates to initialize your circuit. The AmplitudeEmbedding template might be particularly useful in this case.

Please let me know if this helps!

Hi @CatalinaAlbornoz, I am using qc.initialize with input data, so it can not use a static state. Using qml.AmplitudeEmbedding does work. I don’t remember why I wasn’t using it initially. Anyway, I am getting faster results while using Qiskit directly, so I’ll be using that for now.

Hi @ankit27kh, I’m glad that AmplitudeEmbedding works and that you’re getting faster results!

Hello,

sorry for the late message on the topic, but you said you were directly using Qiskit to get your results. DOes that mean you are using the Adam optimizer from qiskit ? Would you mind sharing your code for that please, I am trying to dosomething similar but can’t find any good documentation online

thanks !

Hi @Julien_Bruyninckx, I used Qiskit’s SPSA optimiser for my results.
The way to use SPSA and Adam is different as far as I remember. But all optimisers have an optimize method that you need to use. The documentation page of each optimizer provides the details of the arguments required for the optimize method.

Hello,

Thanks for the reply.

I understand that part, but then can the optimize function include a quantum circuit similarly to your code ?
That is the cost/loss function is calculated with the value from a quantum circuit, which means in order to perform gradient descent, the optimizer will need to calculate the gradient of said quantum circuit ?

Also, how did you calculated expectation values in Qiskit please ? there is no direct way to do it andI’m really struggling to move my code to qiskit.

I made a post about how my qml code didnt work with qiskit.aer ( this is not really related to my question), but it contains my code , and its a similar task to yours.

I used the expectation value of my circuit as the cost for the optimisation function.
The expectation value can be calculated as:

ep = (res.get("1", 0) - res.get("0", 0)) / shots

where, res is the counts dictionary received from the circuit.

I did not use PennyLane for this one, and everything was contained within Qiskit.
The Qiskit optimisers can take Qiskit circuits to optimise, and you do not need to specify the gradient function.