Cost functions
Die Siite isch nonig ibersetzt worre. Ihr luege d englischi Originalversion aa.
During this lesson, we'll learn how to evaluate a cost function:
- First, we'll learn about Qiskit Runtime primitives
- Define a cost function . This is a problem-specific function that defines the problem's goal for the optimizer to minimize (or maximize)
- Defining a measurement strategy with the Qiskit Runtime primitives to optimize speed versus accuracy
Primitives
All physical systems, whether classical or quantum, can exist in different states. For example, a car on a road can have a certain mass, position, speed, or acceleration that characterize its state. Similarly, quantum systems can also have different configurations or states, but they differ from classical systems in how we deal with measurements and state evolution. This leads to unique properties such as superposition and entanglement that are exclusive to quantum mechanics. Just like we can describe a car's state using physical properties like speed or acceleration, we can also describe the state of a quantum system using observables, which are mathematical objects.
In quantum mechanics, states are represented by normalized complex column vectors, or kets (), and observables are Hermitian linear operators () that act on the kets. An eigenvector () of an observable is known as an eigenstate. Measuring an observable for one of its eigenstates () will give us the corresponding eigenvalue () as readout.
If you're wondering how to measure a quantum system and what you can measure, Qiskit offers two primitives that can help:
Sampler: Given a quantum state , this primitive obtains the probability of each possible computational basis state.Estimator: Given a quantum observable and a state , this primitive computes the expected value of .
The Sampler primitive
The Sampler primitive calculates the probability of obtaining each possible state from the computational basis, given a quantum circuit that prepares the state . It calculates
Where is the number of qubits, and the integer representation of any possible output binary string (that is, integers base ).
The Qiskit Runtime Sampler runs the circuit multiple times on a quantum device, performing measurements on each run, and reconstructing the probability distribution from the recovered bitstrings. The more runs (or shots) it performs, the more accurate the results will be, but this requires more time and quantum resources.
However, since the number of possible outputs grows exponentially with the number of qubits (that is, ), the number of shots will need to grow exponentially as well in order to capture a dense probability distribution. Therefore, Sampler is only efficient for sparse probability distributions; where the target state must be expressible as a linear combination of the computational basis states, with the number of terms growing at most polynomially with the number of qubits:
The Sampler can also be configured to retrieve probabilities from a subsection of the circuit, representing a subset of the total possible states.
The Estimator primitive
The Estimator primitive calculates the expectation value of an observable for a quantum state ; where the observable probabilities can be expressed as , being the eigenstates of the observable . The expectation value is then defined as the average of all possible outcomes (that is, the eigenvalues of the observable) of a measurement of the state , weighted by the corresponding probabilities:
However, calculating the expectation value of an observable is not always possible, as we often don't know its eigenbasis. The Qiskit Runtime Estimator uses a complex algebraic process to estimate the expectation value on a real quantum device by breaking down the observable into a combination of other observables whose eigenbasis we do know.
In simpler terms, Estimator breaks down any observable that it doesn't know how to measure into simpler, measurable observables called Pauli operators.
Any operator can be expressed as a combination of Pauli operators.
such that
where is the number of qubits, for (that is, integers base ), and .
After performing this decomposition, Estimator derives a new circuit for each observable (from the original circuit), to effectively diagonalize the Pauli observable in the computational basis and measure it. We can easily measure Pauli observables because we know ahead of time, which is not the case generally for other observables.
For each , the Estimator runs the corresponding circuit on a quantum device multiple times, measures the output state in the computational basis, and calculates the probability of obtaining each possible output . It then looks for the eigenvalue of corresponding to each output , multiplies by , and adds all the results together to obtain the expected value of the observable for the given state .
Since calculating the expectation value of Paulis is impractical (that is, exponentially growing), Estimator can only be efficient when a large amount of are zero (that is, sparse Pauli decomposition instead of dense). Formally we say that, for this computation to be efficiently solvable, the number of non-zero terms has to grow at most polynomially with the number of qubits :
The reader may notice the implicit assumption that probability sampling also needs to be efficient as explained for Sampler, which means
Guided example to calculate expectation values
Let's assume the single-qubit state , and observable
with the following theoretical expectation value
Since we do not know how to measure this observable, we cannot compute its expectation value directly, and we need to re-express it as . Which can be shown to evaluate to the same result by virtue of noting that , and .
Let see how to compute and directly. Since and do not commute (that is, they don't share the same eigenbasis), they cannot be measured simultaneously, therefore we need the auxiliary circuits:
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-aer qiskit-ibm-runtime rustworkx
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
# The following code will work for any other initial single-qubit state and observable
original_circuit = QuantumCircuit(1)
original_circuit.h(0)
H = SparsePauliOp(["X", "Z"], [2, -1])
aux_circuits = []
for pauli in H.paulis:
aux_circ = original_circuit.copy()
aux_circ.barrier()
if str(pauli) == "X":
aux_circ.h(0)
elif str(pauli) == "Y":
aux_circ.sdg(0)
aux_circ.h(0)
else:
aux_circ.id(0)
aux_circ.measure_all()
aux_circuits.append(aux_circ)
original_circuit.draw("mpl")
# Auxiliary circuit for X
aux_circuits[0].draw("mpl")
# Auxiliary circuit for Z
aux_circuits[1].draw("mpl")
We can now carry out the computation manually using Sampler and check the results on Estimator:
from qiskit.primitives import StatevectorSampler, StatevectorEstimator
from qiskit.result import QuasiDistribution
import numpy as np
## SAMPLER
shots = 10000
sampler = StatevectorSampler()
job = sampler.run(aux_circuits, shots=shots)
# Run the sampler job and step through results
expvals = []
for index, pauli in enumerate(H.paulis):
data_pub = job.result()[index].data
bitstrings = data_pub.meas.get_bitstrings()
counts = data_pub.meas.get_counts()
quasi_dist = QuasiDistribution(
{outcome: freq / shots for outcome, freq in counts.items()}
)
# Use the probabilities and known eigenvalues of Pauli operators to estimate the expectation value.
val = 0
if str(pauli) == "X":
val += -1 * quasi_dist.get(1, 0)
val += 1 * quasi_dist.get(0, 0)
if str(pauli) == "Y":
val += -1 * quasi_dist.get(1, 0)
val += 1 * quasi_dist.get(0, 0)
if str(pauli) == "Z":
val += 1 * quasi_dist.get(0, 0)
val += -1 * quasi_dist.get(1, 0)
expvals.append(val)
# Print expectation values
print("Sampler results:")
for pauli, expval in zip(H.paulis, expvals):
print(f" >> Expected value of {str(pauli)}: {expval:.5f}")
total_expval = np.sum(H.coeffs * expvals).real
print(f" >> Total expected value: {total_expval:.5f}")
# Use estimator for comparison
observables = [
*H.paulis,
H,
] # Note: run for individual Paulis as well as full observable H
estimator = StatevectorEstimator()
job = estimator.run([(original_circuit, observables)])
estimator_expvals = job.result()[0].data.evs
# Print results
print("Estimator results:")
for obs, expval in zip(observables, estimator_expvals):
if obs is not H:
print(f" >> Expected value of {str(obs)}: {expval:.5f}")
else:
print(f" >> Total expected value: {expval:.5f}")
Sampler results:
>> Expected value of X: 1.00000
>> Expected value of Z: 0.00420
>> Total expected value: 1.99580
Estimator results:
>> Expected value of X: 1.00000
>> Expected value of Z: 0.00000
>> Total expected value: 2.00000
Mathematical rigor (optional)
Expressing with respect to the basis of eigenstates of , , it follows:
Since we do not know the eigenvalues or eigenstates of the target observable , first we need to consider its diagonalization. Given that is Hermitian, there exists a unitary transformation such that where is the diagonal eigenvalue matrix, so if , and .
This implies that the expected value can be rewritten as:
Given that if a system is in the state the probability of measuring is , the above expected value can be expressed as:
It is very important to note that the probabilities are taken from the state instead of . This is why the matrix is absolutely necessary. You might be wondering how to obtain the matrix and the eigenvalues . If you already had the eigenvalues, then there would be no need to use a quantum computer since the goal of variational algorithms is to find these eigenvalues of .
Fortunately, there is a way around that: any matrix can be written as a linear combination of tensor products of Pauli matrices and identities, all of which are both hermitian and unitary with known and . This is what Runtime's Estimator does internally by decomposing any Operator object into a SparsePauliOp.
Here are the Operators that can be used:
So let's rewrite with respect to the Paulis and identities:
where for (that is, base ), and :
where and , such that:
Cost functions
In general, cost functions are used to describe the goal of a problem and how well a trial state is performing with respect to that goal. This definition can be applied to various examples in chemistry, machine learning, finance, optimization, and so on.
Let's consider a simple example of finding the ground state of a system. Our objective is to minimize the expectation value of the observable representing energy (Hamiltonian ):