Result types - Amazon Braket

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 the Observable 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

OQC

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])

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