> ## Documentation Index
> Fetch the complete documentation index at: https://docs.classiq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Simplified Quantum Layer (QLayerV2)

`QLayerV2` is a new, simplified quantum layer for hybrid QNNs. It is constructed from a synthesized quantum program and an optional set of observables, and produces a trainable `torch.nn.Module` whose outputs are the expectation values of those observables.

The new interface is designed to be more intuitive than the classic `QLayer` and - just as importantly - it is **much faster** behind the scenes, so training loops run considerably quicker.

Use it when your quantum layer's output is naturally a set of expectation values. For layers that need sampling, counts, or custom classical post-processing, keep using the classic [`QLayer`](/user-guide/applications/qml/qnn/qlayer) (see [Choosing between the two layers](#choosing-between-the-two-layers)).

<Info>
  `QLayerV2` currently runs only inside **Classiq Studio** and is limited to circuits of **up to 12 qubits**. Constructing it in any other environment, or with a larger circuit, raises an error. Extending its scale and reach is planned.
</Info>

<Note>
  **`QLayerV2` is a temporary name.** It ships alongside the existing `QLayer` (also available under the alias `QLayerV1`), which is unchanged for now so existing code keeps working. `QLayerV2` is the interface we are moving toward: once it covers the full feature set, the classic interface will go through the standard deprecation process and `QLayerV2` will become the single, canonical `QLayer`. That deprecation has **not** started yet.
</Note>

## Interface

`QLayerV2` is imported from the same package as `QLayer`:

[comment]: DO_NOT_TEST

```python theme={null}
from classiq.applications.qnn import QLayerV2

QLayerV2(
    qprog,             # a synthesized QuantumProgram
    observables=None,  # optional; defaults to one <Z_i> per qubit
)
```

* `qprog` - a synthesized [`QuantumProgram`](/user-guide/synthesis/index). Its parameters are classified by name: the `input`/`i_` prefixes mark **inputs** (the encoded classical data) and the `weight`/`w_` prefixes mark **trainable weights** (for example `input_0`, `i_x`, `weight_0`, `w_theta`). See [Parameters: Inputs Versus Weights](/user-guide/applications/qml/qnn/qnn#parameters-inputs-versus-weights).
* `observables` - one output feature per Pauli observable. Defaults to one `<Z_i>` per qubit, so the default output width equals the circuit width. Accepts a single `SparsePauliOp` or a sequence of them. An observable narrower than the program is padded with identities to the program's width.

The layer trains like any other `torch` module. Its `forward` is rank-preserving: an input of shape `(batch, in_features)` produces `(batch, out_features)`, and an unbatched input of shape `(in_features,)` produces `(out_features,)`.

## Examples

### Minimal example

With only a `qprog`, the layer measures one `<Z_i>` per qubit using the default observables:

[comment]: DO_NOT_TEST

```python theme={null}
from classiq import *
from classiq.applications.qnn import QLayerV2


@qfunc
def main(input_0: CReal, weight_0: CReal, res: Output[QArray]) -> None:
    allocate(2, res)
    RY(input_0, res[0])   # driven by the input feature
    RY(weight_0, res[1])  # trainable weight
    CX(res[0], res[1])


qprog = synthesize(main)

# Defaults: one <Z_i> per qubit.
layer = QLayerV2(qprog)
```

The result is a trainable `torch` layer that plugs into an ordinary network:

[comment]: DO_NOT_TEST

```python theme={null}
import torch.nn as nn


class MyNet(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        self.pre = nn.Linear(4, 1)
        self.quantum = QLayerV2(qprog)

    def forward(self, x):
        x = self.pre(x)
        return self.quantum(x)
```

### Custom observables

Pass one `SparsePauliOp` per output feature to control what the layer measures. Here the single output feature is `<Z_0>`:

[comment]: DO_NOT_TEST

```python theme={null}
from classiq.qmod.builtins.enums import Pauli

# <Z_0>: Pauli-Z on qubit 0. Narrower observables are padded to the circuit width.
z0 = Pauli.Z(0)

layer = QLayerV2(qprog, observables=[z0])
```

## Choosing between the two layers

The classic [`QLayer`](/user-guide/applications/qml/qnn/qlayer) is also available under the alias `QLayerV1`, so the two interfaces can be named side by side during the transition.

|             | [`QLayerV1`](/user-guide/applications/qml/qnn/qlayer) (alias of the classic `QLayer`) | `QLayerV2` (new)                          |
| ----------- | ------------------------------------------------------------------------------------- | ----------------------------------------- |
| Output      | Any tensor from a custom `post_process`                                               | Expectation values of Pauli observables   |
| You provide | `execute` and `post_process` callables                                                | Just `qprog` (and optional `observables`) |
| Measurement | Sampling derived values                                                               | Expectation values only                   |
| Speed       | Standard                                                                              | Much faster                               |
| Runs        | Anywhere execution is available                                                       | Classiq Studio only, up to 12 qubits      |

`QLayerV1` remains available today for sampling, counts, and custom post-processing. `QLayerV2` is the interface we are building toward and will eventually become the single, canonical `QLayer`. Reach for `QLayerV2` when you want the simplest, fastest path to an expectation-value layer.
