qiskit-community / qiskit-machine-learning

Quantum Machine Learning
https://qiskit-community.github.io/qiskit-machine-learning/
Apache License 2.0
666 stars 325 forks source link

Separate parameters for the trainable part and the encoding part in EstimatorQNN #820

Open Pingal-Pratyush-Nath opened 3 months ago

Pingal-Pratyush-Nath commented 3 months ago

What should we add?

The circuit that we pass in the estimator QNN contains two parts : the encoding part(non trainable) and the trainable part. If the encoding circuit changes for every data point, we will have to reinstantiate the model every-time. It would have been better if we can change that encoding part of the model every time we call the model. I know we can create a parameterized circuit and pass in the parameters everytime, but sometimes the circuit itself changes depending on the data point and it is difficult to use Torch Connector for that.

I have added a sample code.

def initial_state(data)->QuantumCircuit:
    graph = to_networkx(data)

    adj_mat = nx.adjacency_matrix(graph).todense()
    adj_circ = GraphState(adj_mat)
    adj_circ.barrier()
    for i in range(adj_circ.num_qubits):
        adj_circ.rz(2*np.pi*float(data.z[i])/9,i)
    edge_attr = egde_attr_encoding(data)
    for i in range(len(edge_attr)):
        adj_circ.crz(edge_attr[i][0],edge_attr[i][1],edge_attr[i][2])

    adj_circ.decompose()

    return adj_circ

qc = initial_state(data_val)
ansatz = EfficientSU2(num_qubits = 5, num_reps = 2)
qc_final = ansatz.compose(qc)

qnn= EstimatorQNN(
    circuit=qc_final, input_params=qc.parameters, weight_params=ansatz.parameters
)

initial_weights = 0.1 * (2 * algorithm_globals.random.random(qnn1.num_weights) - 1)
model = TorchConnector(qnn, initial_weights=initial_weights)
edoaltamura commented 3 days ago

Thanks for pointing this out @Pingal-Pratyush-Nath, this is an interesting and tricky problem! Currently, EstimatorQNN only supports dynamic parameters but not a feature map with a fully dynamic circuit structure. In my view, this would be achieved in the following way.

Change the EstimatorQNN arguments to take a (constant) ansatz: QuantumCircuit and a variable feature_map: QuantumCircuit | Callable[[Any], QuantumCircuit] similar to QNNCircuit.

Note that changing the structure of the circuit for every data point (or GraphState) means that you have to transpile the composed circuit every time, if you want to run on real or emulated hardware, for two reasons (i) to optimise gates and pulses and (ii) to convert the circuit to the backend’s (Instruction Set Architecture (ISA))[https://www.ibm.com/quantum/blog/isa-circuits]. Potentially, this is an expensive operation to repeat every time, so you may want to keep an eye on this step if you're after performance/speed.

Currently, we do not have a plan for including dynamic feature maps in EstimatorQNN, however, I warmly encourage you to share your ideas and code snippets in this threads so that us and the rest of the community can make suggestions.