Result types
Amazon Braket can return different types of results when a circuit is
measured using ResultType
. A circuit can return the following types of
results.
-
AdjointGradient
returns the gradient (vector derivative) of the expectation value of a provided observable. This observable is acting on a provided target with respect to specified parameters using the adjoint differentiation method. You can only use this method when shots=0. -
Amplitude
returns the amplitude of specified quantum states in the output wave function. It is available on the SV1 and local simulators only. -
Expectation
returns the expectation value of a given observable, which can be specified with theObservable
class introduced later in this chapter. The target qubits used to measure the observable must be specified, and the number of specified targets must equal the number of qubits on which the observable acts. If no targets are specified, the observable must operate only on 1 qubit and it is applied to all qubits in parallel. -
Probability
returns the probabilities of measuring computational basis states. If no targets are specified,Probability
returns the probability of measuring all basis states. If targets are specified, only the marginal probabilities of the basis vectors on the specified qubits are returned. -
Reduced density matrix
returns a density matrix for a subsystem of specified target qubits from a system of qubits. To limit the size of this result type, Braket limits the number of target qubits to a maximum of 8. -
StateVector
returns the full state vector. It is available on the local simulator. -
Sample
returns the measurement counts of a specified target qubit set and observable. If no targets are specified, the observable must operate only on 1 qubit and it is applied to all qubits in parallel. If targets are specified, the number of specified targets must equal the number of qubits on which the observable acts. -
Variance
returns the variance (mean([x-mean(x)]2)
) of the specified target qubit set and observable as the requested result type. If no targets are specified, the observable must operate only on 1 qubit and it is applied to all qubits in parallel. Otherwise, the number of targets specified must equal the number of qubits to which the observable can be applied.
The supported result types for different devices:
Local sim |
SV1 |
DM1 |
TN1 |
Rigetti |
IonQ |
IQM |
|
Adjoint Gradient |
N |
Y |
N |
N |
N |
N |
N |
Amplitude |
Y |
Y |
N |
N |
N |
N |
N |
Expectation |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Probability |
Y |
Y |
Y |
N |
Y* |
Y |
Y |
Reduced density matrix |
Y |
N |
Y |
N |
N |
N |
N |
State vector |
Y |
N |
N |
N |
N |
N |
N |
Sample |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Variance |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Note
* Rigetti only supports probability result types of up to 40 qubits.
You can check the supported result types by examining the device properties, as shown in the following example.
device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Aspen-M-3") # print the result types supported by this device for iter in device.properties.action['braket.ir.jaqcd.program'].supportedResultTypes: print(iter)
name='Sample' observables=['x', 'y', 'z', 'h', 'i'] minShots=10 maxShots=100000 name='Expectation' observables=['x', 'y', 'z', 'h', 'i'] minShots=10 maxShots=100000 name='Variance' observables=['x', 'y', 'z', 'h', 'i'] minShots=10 maxShots=100000 name='Probability' observables=None minShots=10 maxShots=100000
To call a ResultType
, append it to a circuit, as shown in the following
example.
from braket.circuits import Observable circ = Circuit().h(0).cnot(0, 1).amplitude(state=["01", "10"]) circ.probability(target=[0, 1]) circ.probability(target=0) circ.expectation(observable=Observable.Z(), target=0) circ.sample(observable=Observable.X(), target=0) circ.state_vector() circ.variance(observable=Observable.Z(), target=0) # print one of the result types assigned to the circuit print(circ.result_types[0])
Note
Some devices provide measurements (for instance Rigetti) as results
and others provide probabilities as results (for instance IonQ).
The SDK provides a measurements property on results, but for
the devices that return probabilities, it is post-computed. Thus, devices like those
provided by IonQ have measurement results
determined by probability since per shot measurements are not returned. You can check
if a result is post-computed by viewing the
measurements_copied_from_device
on the result object as shown in this
file
Observables
Amazon Braket includes an Observable
class, which can be
used to specify an observable to be measured.
You can apply at most one unique non-identity observable to each qubit. If you specify two or more different non-identity observables to the same qubit, you see an error. For this purpose, each factor of a tensor product counts as an individual observable, so it is permissible to have multiple tensor products acting on the same qubit, provided that the factor acting on that qubit is the same.
You can also scale an observable and add observables (scaled or not). This creates a
Sum
which can be used in the AdjointGradient
result
type.
The Observable
class includes the following observables.
Observable.I() Observable.H() Observable.X() Observable.Y() Observable.Z() # get the eigenvalues of the observable print("Eigenvalue:", Observable.H().eigenvalues) # or whether to rotate the basis to be computational basis print("Basis rotation gates:",Observable.H().basis_rotation_gates) # get the tensor product of observable for the multi-qubit case tensor_product = Observable.Y() @ Observable.Z() # view the matrix form of an observable by using print("The matrix form of the observable:\n",Observable.Z().to_matrix()) print("The matrix form of the tensor product:\n",tensor_product.to_matrix()) # also factorize an observable in the tensor form print("Factorize an observable:",tensor_product.factors) # self-define observables given it is a Hermitian print("Self-defined Hermitian:",Observable.Hermitian(matrix=np.array([[0, 1],[1, 0]]))) print("Sum of other (scaled) observables:", 2.0 * Observable.X() @ Observable.X() + 4.0 * Observable.Z() @ Observable.Z())
Eigenvalue: [ 1 -1] Basis rotation gates: (Ry('angle': -0.7853981633974483, 'qubit_count': 1),) The matrix form of the observable: [[ 1.+0.j 0.+0.j] [ 0.+0.j -1.+0.j]] The matrix form of the tensor product: [[ 0.+0.j 0.+0.j 0.-1.j 0.-0.j] [ 0.+0.j -0.+0.j 0.-0.j 0.+1.j] [ 0.+1.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j -0.-1.j 0.+0.j -0.+0.j]] Factorize an observable: (Y('qubit_count': 1), Z('qubit_count': 1)) Self-defined Hermitian: Hermitian('qubit_count': 1, 'matrix': [[0.+0.j 1.+0.j], [1.+0.j 0.+0.j]]) Sum of other (scaled) observables: Sum(TensorProduct(X('qubit_count': 1), X('qubit_count': 1)), TensorProduct(Z('qubit_count': 1), Z('qubit_count': 1)))
Parameters
Circuits may include free parameters, which you can be use in a “construct once - run many times” manner and to compute gradients. Free parameters have a string-encoded name that you can use to specify their values or to determine whether to differentiate with respect to them.
from braket.circuits import Circuit, FreeParameter, Observable theta = FreeParameter("theta") phi = FreeParameter("phi") circ = Circuit().h(0).rx(0, phi).ry(0, phi).cnot(0, 1).xx(0, 1, theta) circ.adjoint_gradient(observable=Observable.Z() @ Observable.Z(), target=[0, 1], parameters = ["phi", theta]
For the parameters you want to differentiate, specify them either by using their name
(as a string) or by direct reference. Note that computing the gradient using the
AdjointGradient
result type is done with respect to the expectation value of the observable.
Note: If you have fixed the values of free parameters
by passing them as arguments to the parameterized circuit, running a circuit with
AdjointGradient
as a result type and parameters specified will produce
an error. This is because the parameters we are using to differentiate with are no
longer present. See the following example.
device.run(circ(0.2), shots=0) # will error, as no free parameters will be present device.run(circ, shots=0, inputs={'phi'=0.2, 'theta'=0.2) # will succeed