# Computing Expectation Values¶

CUDA-Q provides generic library functions enabling one to compute expectation values of quantum spin operators with respect to a parameterized CUDA-Q kernel. Let’s take a look at an example of this:

```# The example here shows a simple use case for the `cudaq::observe``
# function in computing expected values of provided spin operators.

import cudaq
from cudaq import spin

@cudaq.kernel
def kernel(theta: float):
qvector = cudaq.qvector(2)
x(qvector[0])
ry(theta, qvector[1])
x.ctrl(qvector[1], qvector[0])

spin_operator = 5.907 - 2.1433 * spin.x(0) * spin.x(1) - 2.1433 * spin.y(
0) * spin.y(1) + .21829 * spin.z(0) - 6.125 * spin.z(1)

# Pre-computed angle that minimizes the energy expectation of the `spin_operator`.
angle = 0.59

energy = cudaq.observe(kernel, spin_operator, angle).expectation()
print(f"Energy is {energy}")
```

Here we define a parameterized CUDA-Q kernel that takes an angle, `theta`, as a single input. This angle becomes the argument of a single `ry` rotation.

We define a Hamiltonian operator via the CUDA-Q `cudaq.SpinOperator` type.

CUDA-Q provides a generic function `cudaq.observe`. This function takes as input three arguments. The first two argument are a parameterized kernel and the `cudaq.SpinOperator` whose expectation value we wish to compute. The final arguments are the runtime parameters at which we evaluate the parameterized kernel.

```// Compile and run with:
// ```
// nvq++ expectation_values.cpp -o d2.x && ./d2.x
// ```

#include <cudaq.h>
#include <cudaq/algorithm.h>

// The example here shows a simple use case for the `cudaq::observe`
// function in computing expected values of provided spin_ops.

struct ansatz {
auto operator()(double theta) __qpu__ {
cudaq::qvector q(2);
x(q[0]);
ry(theta, q[1]);
x<cudaq::ctrl>(q[1], q[0]);
}
};

int main() {

// Build up your spin op algebraically
using namespace cudaq::spin;
cudaq::spin_op h = 5.907 - 2.1433 * x(0) * x(1) - 2.1433 * y(0) * y(1) +
.21829 * z(0) - 6.125 * z(1);

// Observe takes the kernel, the spin_op, and the concrete
// parameters for the kernel
double energy = cudaq::observe(ansatz{}, h, .59);
printf("Energy is %lf\n", energy);
return 0;
}
```

Here we define a parameterized CUDA-Q kernel, a callable type named `ansatz` that takes as input a single angle `theta`. This angle becomes the argument of a single `ry` rotation.

In host code, we define a Hamiltonian operator via the CUDA-Q `spin_op` type. CUDA-Q provides a generic function `cudaq::observe`. This function takes as input three terms. The first two terms are a parameterized kernel and the `spin_op` whose expectation value we wish to compute. The last term contains the runtime parameters at which we evaluate the parameterized kernel.

The return type of this function is an `cudaq::observe_result` which contains all the data from the execution, but is trivially convertible to a double, resulting in the expectation value we are interested in.

To compile and execute this code, we run the following:

```nvq++ expectation_values.cpp -o exp_vals.x
./exp_vals.x
```