---
title: "ClikaRT::nn::QConvWoQ"
sidebar_label: "QConvWoQ"
description: "Weight-only-quantized convolution module: the weight rests quantized (a QTensor) and decodes inside the kernels; compute runs at the activation dtype, Conv's weight-only sibling."
---

<!-- Generated by tools/doxygen2mdx from Doxygen XML. Do not edit. -->

<span class="api-kind api-kind--class">class</span>

**Header:** `ClikaRT/nn/qconv_woq.h`  
**Inherits:** [`ClikaRT::nn::Module`](./Module.md)

A bound, weight-packing weight-only-quantized convolution, the public face of the runtime's WoQ conv, exposed as an `nn::Module` leaf. The weight arrives quantized (a `QTensor`: codes + scheme) and STAYS quantized at rest; the first `forward` packs it into the backend's fused layout where the geometry/scheme admit one (the fused path computes straight from the quantized bytes), or serves the exact dequant fallback otherwise. Build one per layer at load, reuse it every step.

The lifecycle (uniform across every weight-bearing `nn` module):

1. `make(in, out, kernel, options, ...)`, the ONE constructor: declares storage-free `weight` / `bias` slots under their canonical names.
2. `set_weights(qw[, b])` or `load_state_dict(...)` binds; a quantized payload lands as-is (its scheme travels with it).
3. `forward(x)` packs ON FIRST CALL (once, thread-safe), then serves the packed form every step; `LoadOptions::pack_on_load` pays the pack at load time.

Layout: activations are channels-last `[N, D1..Dn, C]`; the weight's logical shape is OHWI `[out_channels, K1..Kn, in_channels/groups]` (the quantized codes' element geometry). Held via `std::shared_ptr` (an `nn::Module` leaf); copy/move are pinned by the base.

Weight-only-quantized convolution module: the weight rests quantized (a [`QTensor`](../QTensor.md)) and decodes inside the kernels; compute runs at the activation dtype, [`Conv`](./Conv.md)'s weight-only sibling.

Layouts follow [`Conv`](./Conv.md) (channels-last, OHWI weight). The module owns its weights: construct with `make(...)`, or declare shapes and bind a checkpoint via [`load_state_dict`](./Module.md#load_state_dict). The first forward packs the weight (or [`LoadOptions::pack_on_load`](./LoadOptions.md#pack_on_load) packs it at the end of the load); the pack is the one resident copy, and [`named_parameters()`](./Module.md#named_parameters) / [`state_dict()`](./Module.md#state_dict) read the weight back from it; `to(dtype)` restores, casts, and re-packs on the next forward.

## Static member functions

### make() {#make}

<div class="api-signature">

`static` `std::shared_ptr<QConvWoQ>` **`make`**(<br />
&nbsp;&nbsp;&nbsp;&nbsp;`std::int64_t` `in_channels`,<br />
&nbsp;&nbsp;&nbsp;&nbsp;`std::int64_t` `out_channels`,<br />
&nbsp;&nbsp;&nbsp;&nbsp;[`Span`](../Span.md)`<const std::int64_t>` `kernel`,<br />
&nbsp;&nbsp;&nbsp;&nbsp;[`Span`](../Span.md)`<const std::int64_t>` `stride` `=` `{}`,<br />
&nbsp;&nbsp;&nbsp;&nbsp;[`Span`](../Span.md)`<const std::int64_t>` `padding` `=` `{}`,<br />
&nbsp;&nbsp;&nbsp;&nbsp;[`Span`](../Span.md)`<const std::int64_t>` `dilation` `=` `{}`,<br />
&nbsp;&nbsp;&nbsp;&nbsp;`std::int64_t` `groups` `=` `1`,<br />
&nbsp;&nbsp;&nbsp;&nbsp;`bool` `bias` `=` `false`,<br />
&nbsp;&nbsp;&nbsp;&nbsp;[`ops::PadMode`](../ops/index.md#PadMode) `mode` `=` [`ops::PadMode::Constant`](../ops/index.md#PadMode),<br />
&nbsp;&nbsp;&nbsp;&nbsp;`std::optional<double>` `value` `=` `std::nullopt`,<br />
&nbsp;&nbsp;&nbsp;&nbsp;`std::optional<`[`ops::Activation`](../ops/index.md#Activation)`>` `activation` `=` `std::nullopt`,<br />
&nbsp;&nbsp;&nbsp;&nbsp;[`DataType`](../../index.md#DataType) `dtype` `=` [`DataType::Float32`](../../index.md#DataType),<br />
&nbsp;&nbsp;&nbsp;&nbsp;[`Device`](../Device.md) `device` `=` [`Device::cpu`](../Device.md#cpu)`()`<br />
)

</div>

Defaults: `stride` / `padding` / `dilation` = empty (unit stride, no pad, unit dilation), `groups` = `1`, `bias` = `false` (no bias slot), `mode` = `PadMode::Constant`, `value` = unset (zero fill), `activation` = none, [`dtype`](../dtype/index.md) = `Float32`, [`device`](../device-namespace/index.md) = CPU.

**Throws**

- `ClikaRT::Error`: as stated above.

<div class="api-declared">

Declared in `ClikaRT/nn/qconv_woq.h`, line 72

</div>

## Member functions

### \~QConvWoQ() {#QConvWoQ}

<div class="api-signature">

**`~QConvWoQ`**() `override`

</div>

<div class="api-declared">

Declared in `ClikaRT/nn/qconv_woq.h`, line 86

</div>

### set\_weights() {#set_weights}

<div class="api-signature">

`void` **`set_weights`**([`QTensor`](../QTensor.md) `weight`, [`OptionalTensor`](../../index.md#OptionalTensor) `bias` `=` `{}`)

</div>

Bind the declared slots positionally: `weight` (a quantized [`QTensor`](../QTensor.md) whose logical shape is OHWI `[out, K.., in/groups]`) and, when the module was made with one, `bias` (`[out]`). The quantized payload lands as-is (no dtype cast; quant geometry is scheme-defined; the logical dims are checked). Re-binding after a pack drops the pack. Raises [`ClikaRT::Error`](../Error.md) on a geometry mismatch or a bias without a declared bias slot.

<div class="api-declared">

Declared in `ClikaRT/nn/qconv_woq.h`, line 96

</div>

### to\_impl(StreamOrDevice) {#to_impl}

<div class="api-signature">

`virtual` [`Result`](../Result.md)`<void>` **`to_impl`**([`StreamOrDevice`](../StreamOrDevice.md) `where`) `override`

</div>

Placement move: rebuilds the pack on the target. A dtype cast is Unsupported; the weight stays quantized at rest, and dequantize explicitly if a dense copy is wanted. [`can_cast_to_impl`](./QConvWoQ.md#can_cast_to_impl) reports the same refusal without moving a byte (the tree-wide check behind `Module::to(dtype)`).

<div class="api-declared">

Declared in `ClikaRT/nn/qconv_woq.h`, line 105

</div>

### to\_impl(DataType) {#to_impl-2}

<div class="api-signature">

`virtual` [`Result`](../Result.md)`<void>` **`to_impl`**([`DataType`](../../index.md#DataType) `dtype`) `override`

</div>

<div class="api-declared">

Declared in `ClikaRT/nn/qconv_woq.h`, line 106

</div>

### can\_cast\_to\_impl() {#can_cast_to_impl}

<div class="api-signature">

`virtual` [`Result`](../Result.md)`<void>` **`can_cast_to_impl`**([`DataType`](../../index.md#DataType) `dtype`) `const override`

</div>

Whether THIS module's own slots can be cast to [`dtype`](../dtype/index.md) (children are asked by the tree walk, never here). A failure is the refusal `to(dtype)` would raise; `Module::to(dtype)` asks every module of the tree BEFORE it casts any, so a refused cast leaves the tree untouched and names the leaf. Default: a dense module casts. Override in a leaf that pins its dtype.

<div class="api-declared">

Declared in `ClikaRT/nn/qconv_woq.h`, line 107

</div>

### forward() {#forward}

<div class="api-signature">

[`Tensor`](../Tensor.md) **`forward`**([`Tensor`](../Tensor.md) `x`) `const`

</div>

Apply the bound quantized weight: `x` `[N, D1..Dn, in]` -\> `[N, O1..On, out]` (then bias + activation if configured). The first call packs (once, thread-safe); every later call reuses the pack. Raises [`ClikaRT::Error`](../Error.md) on a shape/dtype mismatch or a still-fake slot.

<div class="api-declared">

Declared in `ClikaRT/nn/qconv_woq.h`, line 115

</div>

## Protected member functions

### initialize\_impl() {#initialize_impl}

<div class="api-signature">

`virtual` [`Result`](../Result.md)`<void>` **`initialize_impl`**() `override`

</div>

The pack hook ([`Module::initialize_impl`](./Module.md#initialize_impl)): packs now (idempotent, thread-safe). Every declared slot must hold a real (loaded) tensor; a still-fake slot is a clean error. [Where](./Where.md) the geometry/scheme admit the fused pack, the packed form becomes the single resident quantized copy and the `weight` slot reads back from it; a geometry the fused path does not serve keeps the quantized weight bound for the exact dequant fallback.

<div class="api-declared">

Declared in `ClikaRT/nn/qconv_woq.h`, line 129

</div>
