<br />

|---|
| [![](https://www.tensorflow.org/images/GitHub-Mark-32px.png) View source on GitHub](https://github.com/tensorflow/quantum/blob/v0.7.6/tensorflow_quantum/python/layers/circuit_executors/expectation.py#L30-L390) |

A Layer that calculates an expectation value.

    tfq.layers.Expectation(
        backend='noiseless', differentiator=None, **kwargs
    )

### Used in the notebooks

| Used in the tutorials |
|---|
| - [Calculate gradients](https://www.tensorflow.org/quantum/tutorials/gradients) - [Hello, many worlds](https://www.tensorflow.org/quantum/tutorials/hello_many_worlds) - [Noise](https://www.tensorflow.org/quantum/tutorials/noise) - [Barren plateaus](https://www.tensorflow.org/quantum/tutorials/barren_plateaus) - [Quantum data](https://www.tensorflow.org/quantum/tutorials/quantum_data) |

<br />

| ## Args ||
|---|---|
| backend (Union\[str, cirq.sim.simulator.SimulatesExpectationValues\], optional): Backend simulator to use. Default is 'noiseless'. ||
| `differentiator` | `Optional[tfq.differentiators.Differentiator]` <br /> Differentiation scheme for gradients. |
| `**kwargs` | Additional keyword arguments for the parent class. |

<br />

<br />

<br />

| ## Input shape ||
|---|---|
| <br /> - circuits: tf.Tensor or list of shape \[batch_size\], each entry a serialized circuit (from tfq.convert_to_tensor). - symbol_names: list or tf.Tensor of shape \[n_symbols\], names of circuit parameters (optional). - symbol_values: tf.Tensor of shape \[batch_size, n_symbols\], values for circuit parameters (optional). - operators: list or tf.Tensor of shape \[n_ops\], observables to measure (optional). - repetitions: int or tf.Tensor, number of measurement repetitions (optional, only for noisy backend). ||

<br />

<br />

<br />

<br />

| ## Output shape ||
|---|---|
| tf.Tensor of shape \[batch_size, n_ops\], expectation values for each circuit and operator. ||

<br />

Given an input circuit and set of parameter values, prepare a quantum state
and output expectation values taken on that state with respect to some
observables to the tensorflow graph.

First define a simple helper function for generating a parametrized
quantum circuit that we will use throughout:

    def _gen_single_bit_rotation_problem(bit, symbols):
        """Generate a toy problem on 1 qubit."""
        starting_state = [0.123, 0.456, 0.789]
        circuit = cirq.Circuit(
            cirq.rx(starting_state[0])(bit),
            cirq.ry(starting_state[1])(bit),
            cirq.rz(starting_state[2])(bit),
            cirq.rz(symbols[2])(bit),
            cirq.ry(symbols[1])(bit),
            cirq.rx(symbols[0])(bit)
        )
        return circuit

In quantum machine learning there are two very common use cases that
align with keras layer constructs. The first is where the circuits
represent the input data points (see the note at the bottom about
using compiled models):

    bit = cirq.GridQubit(0, 0)
    symbols = sympy.symbols('x, y, z')
    ops = [-1.0 * cirq.Z(bit), cirq.X(bit) + 2.0 * cirq.Z(bit)]
    circuit_list = [
        _gen_single_bit_rotation_problem(bit, symbols),
        cirq.Circuit(
            cirq.Z(bit) ** symbols[0],
            cirq.X(bit) ** symbols[1],
            cirq.Z(bit) ** symbols[2]
        ),
        cirq.Circuit(
            cirq.X(bit) ** symbols[0],
            cirq.Z(bit) ** symbols[1],
            cirq.X(bit) ** symbols[2]
        )
    ]
    expectation_layer = tfq.layers.Expectation()
    output = expectation_layer(
        circuit_list, symbol_names=symbols, operators = ops)
    # Here output[i][j] corresponds to the expectation of all the ops
    # in ops w.r.t circuits[i] where keras managed variables are
    # placed in the symbols 'x', 'y', 'z'.
    tf.shape(output)
    tf.Tensor([3 2], shape=(2,), dtype=int32)

Here, different [`cirq.Circuit`](https://quantumai.google/reference/python/cirq/Circuit) instances sharing the common symbols 'x',
'y' and 'z' are used as input. Keras uses the `symbol_names`
argument to map Keras managed variables to these circuits constructed
with `sympy.Symbol`s. Note that you used a Python `list` containing your
circuits, you could also specify a [`tf.keras.Input`](https://www.tensorflow.org/api_docs/python/tf/keras/Input) layer or any
tensorlike object to specify the circuits you would like fed to the layer
at runtime.

Another common use case is where there is a fixed circuit and the
expectation operators vary:

    bit = cirq.GridQubit(0, 0)
    symbols = sympy.symbols('x, y, z')
    ops = [-1.0 * cirq.Z(bit), cirq.X(bit) + 2.0 * cirq.Z(bit)]
    fixed_circuit = _gen_single_bit_rotation_problem(bit, symbols)
    expectation_layer = tfq.layers.Expectation()
    output = expectation_layer(
        fixed_circuit,
        symbol_names=symbols,
        operators=ops,
        initializer=tf.keras.initializers.RandomUniform(0, 2 * np.pi))
    # Here output[i][j] corresponds to
    # the expectation of operators[i][j] w.r.t the circuit where
    # variable values are managed by keras and store numbers in
    # the symbols 'x', 'y', 'z'.
    tf.shape(output)
    tf.Tensor([1 2], shape=(2,), dtype=int32)

Note that in the above examples you used a [`cirq.Circuit`](https://quantumai.google/reference/python/cirq/Circuit) object and a list
of [`cirq.PauliSum`](https://quantumai.google/reference/python/cirq/PauliSum) objects as inputs to your layer. To allow for varying
inputs your could change the line in the above code to:
`expectation_layer(circuit_inputs, symbol_names=symbols, operators=ops)`
with `circuit_inputs` is `tf.keras.Input(shape=(), dtype=tf.dtypes.string)`
to allow you to pass in different circuits in a compiled model. Lastly
you also supplied a `tf.keras.initializer` to the `initializer` argument.
This argument is optional in the case that the layer itself will be managing
the symbols of the circuit and not have them fed in from somewhere else in
the model.

There are also some more complex use cases. Notably these use cases all
make use of the `symbol_values` parameter that causes the
`Expectation` layer to stop managing the `sympy.Symbol`s in the quantum
circuits for the user and instead require them to supply input
values themselves. Lets look at the case where there
is a single fixed circuit, some fixed operators and symbols that must be
common to all circuits:

    bit = cirq.GridQubit(0, 0)
    symbols = sympy.symbols('x, y, z')
    ops = [cirq.Z(bit), cirq.X(bit)]
    circuit = _gen_single_bit_rotation_problem(bit, symbols)
    values = [[1,1,1], [2,2,2], [3,3,3]]
    expectation_layer = tfq.layers.Expectation()
    output = expectation_layer(
        circuit,
        symbol_names=symbols,
        symbol_values=values,
        operators=ops)
    # output[i][j] = The expectation of operators[j] with
    # values[i] placed into the symbols of the circuit
    # with the order specified by symbol_names.
    # so output[1][2] = The expectation of your circuit with parameter
    # values [2,2,2] w.r.t Pauli X.
    output
    tf.Tensor(
    [[0.63005245 0.76338404]
     [0.25707167 0.9632684 ]
     [0.79086655 0.5441111 ]], shape=(3, 2), dtype=float32)

Here is a simple model that uses this particular input signature of
[`tfq.layers.Expectation`](https://www.tensorflow.org/quantum/api_docs/python/tfq/layers/Expectation), that learns to undo the random rotation
of the qubit:

    bit = cirq.GridQubit(0, 0)
    symbols = sympy.symbols('x, y, z')
    circuit = _gen_single_bit_rotation_problem(bit, symbols)
    control_input = tf.keras.Input(shape=(1,))
    circuit_inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string)
    d1 = tf.keras.layers.Dense(10)(control_input)
    d2 = tf.keras.layers.Dense(3)(d1)
    expectation = tfq.layers.Expectation()(
        circuit_inputs, # See note below!
        symbol_names=symbols,
        symbol_values=d2,
        operators=cirq.Z(bit))
    data_in = np.array([[1], [0]], dtype=np.float32)
    data_out = np.array([[1], [-1]], dtype=np.float32)
    model = tf.keras.Model(
        inputs=[circuit_inputs, control_input], outputs=expectation)
    model.compile(
        optimizer=tf.keras.optimizers.Adam(learning_rate=0.01),
        loss=tf.keras.losses.mean_squared_error)
    history = model.fit(
        x=[tfq.convert_to_tensor([circuit] * 2), data_in],
        y=data_out,
        epochs=100)

Lastly `symbol_values`, `operators` and circuit `inputs` can all be fed
Python `list` objects. In addition to this they can also be fed [`tf.Tensor`](https://www.tensorflow.org/api_docs/python/tf/Tensor)
inputs, meaning that you can input all of these things from other Tensor
objects (like `tf.keras.Dense` layer outputs or [`tf.keras.Input`](https://www.tensorflow.org/api_docs/python/tf/keras/Input)s etc).

> [!NOTE]
> **Note:** When specifying a new layer for a *compiled* [`tf.keras.Model`](https://www.tensorflow.org/api_docs/python/tf/keras/Model) using something like `tfq.layers.Expectation()(cirq.Circuit(...), ...)` please be sure to instead use `tfq.layers.Expectation()(circuit_input, ...)` where `circuit_input` is a [`tf.keras.Input`](https://www.tensorflow.org/api_docs/python/tf/keras/Input) that is filled with `tfq.convert_to_tensor([cirq.Circuit(..)] * batch_size)` at runtime. This is because compiled Keras models require non keyword layer `call` inputs to be traceable back to a [`tf.keras.Input`](https://www.tensorflow.org/api_docs/python/tf/keras/Input).

<br />

<br />

<br />

| ## Attributes ||
|---|---|
| `compute_dtype` | The dtype of the computations performed by the layer. |
| `dtype` | Alias of `layer.variable_dtype`. |
| `dtype_policy` | <br /> <br /> |
| `input` | Retrieves the input tensor(s) of a symbolic operation. <br /> Only returns the tensor(s) corresponding to the *first time* the operation was called. |
| `input_dtype` | The dtype layer inputs should be converted to. |
| `input_spec` | <br /> <br /> |
| `losses` | List of scalar losses from `add_loss`, regularizers and sublayers. |
| `metrics` | List of all metrics. |
| `metrics_variables` | List of all metric variables. |
| `non_trainable_variables` | List of all non-trainable layer state. <br /> This extends `layer.non_trainable_weights` to include all state used by the layer including state for metrics and `SeedGenerator`s. |
| `non_trainable_weights` | List of all non-trainable weight variables of the layer. <br /> These are the weights that should not be updated by the optimizer during training. Unlike, `layer.non_trainable_variables` this excludes metric state and random seeds. |
| `output` | Retrieves the output tensor(s) of a layer. <br /> Only returns the tensor(s) corresponding to the *first time* the operation was called. |
| `path` | The path of the layer. <br /> If the layer has not been built yet, it will be `None`. |
| `quantization_mode` | The quantization mode of this layer, `None` if not quantized. |
| `supports_masking` | Whether this layer supports computing a mask using `compute_mask`. |
| `trainable` | Settable boolean, whether this layer should be trainable or not. |
| `trainable_variables` | List of all trainable layer state. <br /> This is equivalent to `layer.trainable_weights`. |
| `trainable_weights` | List of all trainable weight variables of the layer. <br /> These are the weights that get updated by the optimizer during training. |
| `variable_dtype` | The dtype of the state (weights) of the layer. |
| `variables` | List of all layer state, including random seeds. <br /> This extends `layer.weights` to include all state used by the layer including `SeedGenerator`s. Note that metrics variables are not included here, use `metrics_variables` to visit all the metric variables. |
| `weights` | List of all weight variables of the layer. <br /> Unlike, `layer.variables` this excludes metric state and random seeds. |

<br />

## Methods

### `add_loss`

    add_loss(
        loss
    )

Can be called inside of the `call()` method to add a scalar loss.

#### Example:

    class MyLayer(Layer):
        ...
        def call(self, x):
            self.add_loss(ops.sum(x))
            return x

### `add_metric`

    add_metric(
        *args, **kwargs
    )

### `add_variable`

    add_variable(
        shape,
        initializer,
        dtype=None,
        trainable=True,
        autocast=True,
        regularizer=None,
        constraint=None,
        name=None
    )

Add a weight variable to the layer.

Alias of `add_weight()`.

### `add_weight`

    add_weight(
        *args,
        shape=None,
        initializer=None,
        dtype=None,
        trainable=True,
        autocast=True,
        regularizer=None,
        constraint=None,
        aggregation='none',
        overwrite_with_gradient=False,
        name=None
    )

Add a weight variable to the layer.

<br />

<br />

<br />

| Args ||
|---|---|
| `shape` | Shape tuple for the variable. Must be fully-defined (no `None` entries). Defaults to `()` (scalar) if unspecified. |
| `initializer` | Initializer object to use to populate the initial variable value, or string name of a built-in initializer (e.g. `"random_normal"`). If unspecified, defaults to `"glorot_uniform"` for floating-point variables and to `"zeros"` for all other types (e.g. int, bool). |
| `dtype` | Dtype of the variable to create, e.g. `"float32"`. If unspecified, defaults to the layer's variable dtype (which itself defaults to `"float32"` if unspecified). |
| `trainable` | Boolean, whether the variable should be trainable via backprop or whether its updates are managed manually. Defaults to `True`. |
| `autocast` | Boolean, whether to autocast layers variables when accessing them. Defaults to `True`. |
| `regularizer` | Regularizer object to call to apply penalty on the weight. These penalties are summed into the loss function during optimization. Defaults to `None`. |
| `constraint` | Contrainst object to call on the variable after any optimizer update, or string name of a built-in constraint. Defaults to `None`. |
| `aggregation` | Optional string, one of `None`, `"none"`, `"mean"`, `"sum"` or `"only_first_replica"`. Annotates the variable with the type of multi-replica aggregation to be used for this variable when writing custom data parallel training loops. Defaults to `"none"`. |
| `overwrite_with_gradient` | Boolean, whether to overwrite the variable with the computed gradient. This is useful for float8 training. Defaults to `False`. |
| `name` | String name of the variable. Useful for debugging purposes. |

<br />

### `build`

    build(
        input_shape
    )

### `build_from_config`

    build_from_config(
        config
    )

Builds the layer's states with the supplied config dict.

By default, this method calls the `build(config["input_shape"])` method,
which creates weights based on the layer's input shape in the supplied
config. If your config contains other information needed to load the
layer's state, you should override this method.

<br />

<br />

<br />

| Args ||
|---|---|
| `config` | Dict containing the input shape associated with this layer. |

<br />

### `call`

[View source](https://github.com/tensorflow/quantum/blob/v0.7.6/tensorflow_quantum/python/layers/circuit_executors/expectation.py#L290-L390)

    call(
        inputs,
        *,
        symbol_names=None,
        symbol_values=None,
        operators=None,
        repetitions=None,
        initializer=tf.keras.initializers.RandomUniform(0, 2 * np.pi)
    )

Keras call function.

<br />

<br />

<br />

| Args ||
|---|---|
| `inputs` | `tf.Tensor or list` <br /> Circuits to execute, shape \[batch_size\]. |
| `symbol_names` | `list or tf.Tensor, optional` <br /> Names of circuit parameters, shape \[n_symbols\]. |
| `symbol_values` | `tf.Tensor, optional` <br /> Values for circuit parameters, shape \[batch_size, n_symbols\]. |
| `operators` | `list or tf.Tensor, optional` <br /> Observables to measure, shape \[n_ops\]. |
| `repetitions` | `int or tf.Tensor, optional` <br /> Number of measurement repetitions (for noisy backend). |
| `initializer` | `tf.keras.initializers.Initializer, optional` <br /> Initializer for circuit parameters. |

<br />

<br />

<br />

<br />

| Returns ||
|---|---|
| [`tf.Tensor`](https://www.tensorflow.org/api_docs/python/tf/Tensor) | Tensor of shape \[batch_size, n_ops\] that holds the expectation value for each circuit with each op applied to it (after resolving the corresponding parameters in). |

<br />

### `compute_mask`

    compute_mask(
        inputs, previous_mask
    )

### `compute_output_shape`

    compute_output_shape(
        *args, **kwargs
    )

### `compute_output_spec`

    compute_output_spec(
        *args, **kwargs
    )

### `count_params`

    count_params()

Count the total number of scalars composing the weights.

<br />

<br />

<br />

| Returns ||
|---|---|
| An integer count. ||

<br />

### `from_config`

    @classmethod
    from_config(
        config
    )

Creates an operation from its config.

This method is the reverse of `get_config`, capable of instantiating the
same operation from the config dictionary.

> [!NOTE]
> **Note:** If you override this method, you might receive a serialized dtype config, which is a `dict`. You can deserialize it as follows:

    if "dtype" in config and isinstance(config["dtype"], dict):
        policy = dtype_policies.deserialize(config["dtype"])

<br />

<br />

<br />

| Args ||
|---|---|
| `config` | A Python dictionary, typically the output of `get_config`. |

<br />

<br />

<br />

<br />

| Returns ||
|---|---|
| An operation instance. ||

<br />

### `get_build_config`

    get_build_config()

Returns a dictionary with the layer's input shape.

This method returns a config dict that can be used by
`build_from_config(config)` to create all states (e.g. Variables and
Lookup tables) needed by the layer.

By default, the config only contains the input shape that the layer
was built with. If you're writing a custom layer that creates state in
an unusual way, you should override this method to make sure this state
is already created when Keras attempts to load its value upon model
loading.

<br />

<br />

<br />

| Returns ||
|---|---|
| A dict containing the input shape associated with the layer. ||

<br />

### `get_config`

    get_config()

Returns the config of the object.

An object config is a Python dictionary (serializable)
containing the information needed to re-instantiate it.

### `get_weights`

    get_weights()

Return the values of `layer.weights` as a list of NumPy arrays.

### `load_own_variables`

    load_own_variables(
        store
    )

Loads the state of the layer.

You can override this method to take full control of how the state of
the layer is loaded upon calling [`keras.models.load_model()`](https://www.tensorflow.org/api_docs/python/tf/keras/models/load_model).

<br />

<br />

<br />

| Args ||
|---|---|
| `store` | Dict from which the state of the model will be loaded. |

<br />

### `quantize`

    quantize(
        mode=None, type_check=True, config=None
    )

### `quantized_build`

    quantized_build(
        input_shape, mode
    )

### `quantized_call`

    quantized_call(
        *args, **kwargs
    )

### `rematerialized_call`

    rematerialized_call(
        layer_call, *args, **kwargs
    )

Enable rematerialization dynamically for layer's call method.

<br />

<br />

<br />

| Args ||
|---|---|
| `layer_call` | The original `call` method of a layer. |

<br />

<br />

<br />

<br />

| Returns ||
|---|---|
| Rematerialized layer's `call` method. ||

<br />

### `save_own_variables`

    save_own_variables(
        store
    )

Saves the state of the layer.

You can override this method to take full control of how the state of
the layer is saved upon calling `model.save()`.

<br />

<br />

<br />

| Args ||
|---|---|
| `store` | Dict where the state of the model will be saved. |

<br />

### `set_weights`

    set_weights(
        weights
    )

Sets the values of `layer.weights` from a list of NumPy arrays.

### `stateless_call`

    stateless_call(
        trainable_variables,
        non_trainable_variables,
        *args,
        return_losses=False,
        **kwargs
    )

Call the layer without any side effects.

<br />

<br />

<br />

| Args ||
|---|---|
| `trainable_variables` | List of trainable variables of the model. |
| `non_trainable_variables` | List of non-trainable variables of the model. |
| `*args` | Positional arguments to be passed to `call()`. |
| `return_losses` | If `True`, `stateless_call()` will return the list of losses created during `call()` as part of its return values. |
| `**kwargs` | Keyword arguments to be passed to `call()`. |

<br />

<br />

<br />

<br />

| Returns ||
|---|---|
| A tuple. By default, returns `(outputs, non_trainable_variables)`. If `return_losses = True`, then returns `(outputs, non_trainable_variables, losses)`. ||

<br />

> [!NOTE]
> **Note:** `non_trainable_variables` include not only non-trainable weights such as `BatchNormalization` statistics, but also RNG seed state (if there are any random operations part of the layer, such as dropout), and `Metric` state (if there are any metrics attached to the layer). These are all elements of state of the layer.

#### Example:

    model = ...
    data = ...
    trainable_variables = model.trainable_variables
    non_trainable_variables = model.non_trainable_variables
    # Call the model with zero side effects
    outputs, non_trainable_variables = model.stateless_call(
        trainable_variables,
        non_trainable_variables,
        data,
    )
    # Attach the updated state to the model
    # (until you do this, the model is still in its pre-call state).
    for ref_var, value in zip(
        model.non_trainable_variables, non_trainable_variables
    ):
        ref_var.assign(value)

### `symbolic_call`

    symbolic_call(
        *args, **kwargs
    )

### `__call__`

    __call__(
        *args, **kwargs
    )

Call self as a function.