# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # pylint: disable=too-many-arguments,invalid-name,protected-access,unused-argument # ruff: noqa: RUF005 """Builtin Modules.""" from collections.abc import Sequence from tvm import relax as rx from tvm import tirx from . import op from .core import Effect, Module, ModuleList, Parameter, Tensor, get_default_dtype class IOEffect(Effect): """ Modeling IO side effect, for example, printing the content of Tensors on screen, inserting debug breakpoints, etc. """ effect: rx.Var | None def __init__(self): self.effect = None def emit_init(self, name_hint, builder: rx.BlockBuilder) -> list[rx.DataflowVar]: return [builder.emit(rx.op.null_value(), f"{name_hint}.io")] def create(self, name_hint: str) -> list[rx.Var]: assert self.effect is None effect = rx.Var(f"{name_hint}.io", ty=rx.AnyType()) return [effect] def set_state(self, state_vars: list[rx.Var]) -> None: (self.effect,) = state_vars def finalize(self) -> list[rx.Var]: result = self.effect self.effect = None return [result] class ReLU(Module): """Module for ReLU activation layer.""" def forward(self, x: Tensor): return op.relu(x) class SiLU(Module): """Module for SiLU activation layer.""" def forward(self, x: Tensor): return op.silu(x) class GELU(Module): """Module for GELU activation layer.""" def forward(self, x: Tensor): return op.gelu(x) class Identity(Module): """Module that does nothing, sometimes useful for naming purposes.""" def forward(self, x: Tensor): """Forward method for identity. Parameters ---------- x : Tensor The input tensor. Returns ------- Result : Tensor The unchanged input tensor. """ return x class Linear(Module): """Applies a linear transformation :math:`y = xW^T + b`. Parameters ---------- in_features : Union[int, str, tirx.Expr] Size of each input sample. Can be symbolic. out_features : Union[int, str, tirx.Expr] Size of each output sample. Can be symbolic. bias : bool If ``True``, adds a learnable bias. Default: ``True``. dtype : Optional[str] Data type for weight (and bias when *out_dtype* is ``None``). ``None`` uses the default dtype. out_dtype : Optional[str] If set, the matmul accumulates in this dtype and the bias is stored in this dtype instead of *dtype*. Useful for mixed-precision (e.g. ``float32`` accumulation with ``float16`` weights). """ def __init__( self, in_features: int | str | tirx.Expr, out_features: int | str | tirx.Expr, bias: bool = True, dtype: str | None = None, out_dtype: str | None = None, ): super().__init__() self.in_features = in_features self.out_features = out_features self.out_dtype = out_dtype self.weight = Parameter((out_features, in_features), dtype) if bias: self.bias = Parameter((out_features,), dtype=dtype if out_dtype is None else out_dtype) else: self.bias = None def forward(self, x: Tensor) -> Tensor: """ Forward method for linear layer. Parameters ---------- x : Tensor The input tensor. Returns ------- ret : Tensor The output tensor for the linear layer. """ # x: [*B, in_features] # w: [in_features, out_features] w = op.permute_dims(self.weight) # x: [*B, out_features] x = op.matmul(x, w, out_dtype=self.out_dtype) if self.bias is not None: x = x + self.bias return x def to(self, dtype: str | None = None) -> None: """ Override to() such that we do not convert bias if there is `out_dtype`. Otherwise, we might run into dtype mismatch when computing `x + self.bias` since x is of type `out_dtype` and bias becomes `dtype`, potentially different. """ self.weight.to(dtype=dtype) if self.bias is not None and self.out_dtype is None: self.bias.to(dtype=dtype) if dtype is not None and isinstance(getattr(self, "dtype", None), str): self.dtype = dtype # pylint: disable=attribute-defined-outside-init class Conv1D(Module): """Applies a 1D convolution over an input signal. Parameters ---------- in_channels : int Number of channels in the input. out_channels : int Number of channels produced by the convolution. kernel_size : int Size of the convolving kernel. stride : int Stride of the convolution. Default: 1. padding : int Zero-padding added to both sides of the input. Default: 0. dilation : int Spacing between kernel elements. Default: 1. groups : int Number of blocked connections from input to output channels. Default: 1. bias : bool If ``True``, adds a learnable bias. Default: ``True``. dtype : Optional[str] Data type for weight and bias. ``None`` uses the default dtype. """ def __init__( self, in_channels: int, out_channels: int, kernel_size: int, stride: int = 1, padding: int = 0, dilation: int = 1, groups: int = 1, bias: bool = True, dtype: str | None = None, ) -> None: super().__init__() self.in_channels = in_channels self.out_channels = out_channels self.kernel_size = kernel_size self.stride = stride self.padding = padding self.dilation = dilation self.groups = groups self.weight = Parameter( ( self.out_channels, int(self.in_channels // self.groups), self.kernel_size, ), dtype, ) if bias: self.bias = Parameter((self.out_channels,), dtype) else: self.bias = None def forward(self, x: Tensor) -> Tensor: """ Forward method for conv1d layer. Parameters ---------- x : Tensor The input tensor. Returns ------- ret : Tensor The output tensor for the conv1d layer. """ return op.conv1d( x, self.weight, self.bias, self.stride, self.padding, self.dilation, self.groups ) class Conv2D(Module): """Applies a 2D convolution over an input image. Parameters ---------- in_channels : int Number of channels in the input image. out_channels : int Number of channels produced by the convolution. kernel_size : Union[List[int], int] Size of the convolving kernel. An int is expanded to a 2-element list. stride : int Stride of the convolution. Default: 1. padding : int Zero-padding added to both sides of the input. Default: 0. dilation : int Spacing between kernel elements. Default: 1. groups : int Number of blocked connections from input to output channels. Default: 1. bias : bool If ``True``, adds a learnable bias. Default: ``True``. dtype : Optional[str] Data type for weight and bias. ``None`` uses the default dtype. data_layout : str Layout of the input data, e.g. ``"NCHW"`` or ``"NHWC"``. Default: ``"NCHW"``. """ def __init__( # pylint: disable=too-many-arguments self, in_channels: int, out_channels: int, kernel_size: list[int] | int, stride: int = 1, padding: int = 0, dilation: int = 1, groups: int = 1, bias: bool = True, dtype: str | None = None, data_layout: str = "NCHW", ): super().__init__() self.in_channels = in_channels self.out_channels = out_channels self.stride = stride self.padding = padding self.dilation = dilation self.groups = groups self.data_layout = data_layout # Allow dynamic input channels. if isinstance(self.in_channels, int): in_channels = int(self.in_channels / self.groups) else: in_channels = tirx.floordiv(self.in_channels, self.groups) # Expand kernel size if provided an integer. if isinstance(kernel_size, int): self.kernel_size = [kernel_size] * 2 else: self.kernel_size = kernel_size kernel_shape = [self.out_channels, in_channels] + list(self.kernel_size) self.weight = Parameter(kernel_shape, dtype) if bias: self.bias = Parameter((self.out_channels,), dtype) else: self.bias = None def forward(self, x: Tensor) -> Tensor: # pylint: disable=invalid-name """ Forward method for conv2d layer. Parameters ---------- x : Tensor The input tensor. Returns ------- ret : Tensor The output tensor for the conv2d layer. """ return op.conv2d( x, self.weight, self.bias, self.stride, self.padding, self.dilation, self.groups, self.data_layout, ) class Conv3D(Module): """Applies a 3D convolution over an input volume. Parameters ---------- in_channels : int Number of channels in the input volume. out_channels : int Number of channels produced by the convolution. kernel_size : Union[List[int], int] Size of the convolving kernel. An int is expanded to a 3-element list. stride : Union[List[int], int] Stride of the convolution. Default: 1. padding : Union[List[int], int] Zero-padding added to each side of the input. Default: 0. dilation : int Spacing between kernel elements. Default: 1. groups : int Number of blocked connections from input to output channels. Default: 1. bias : bool If ``True``, adds a learnable bias. Default: ``True``. dtype : Optional[str] Data type for weight and bias. ``None`` uses the default dtype. data_layout : str Layout of the input data, e.g. ``"NCDHW"``. Default: ``"NCDHW"``. """ def __init__( # pylint: disable=too-many-arguments self, in_channels: int, out_channels: int, kernel_size: list[int] | int, stride: list[int] | int = 1, padding: list[int] | int = 0, dilation: int = 1, groups: int = 1, bias: bool = True, dtype: str | None = None, data_layout: str = "NCDHW", ): super().__init__() self.in_channels = in_channels self.out_channels = out_channels self.stride = stride self.padding = padding self.dilation = dilation self.groups = groups self.data_layout = data_layout # Allow dynamic input channels. if isinstance(self.in_channels, int): in_channels = int(self.in_channels / self.groups) else: in_channels = tirx.floordiv(self.in_channels, self.groups) # Expand kernel size if given an integer. if isinstance(kernel_size, int): self.kernel_size = [kernel_size] * 3 else: self.kernel_size = kernel_size kernel_shape = [self.out_channels, self.in_channels] + list(self.kernel_size) self.weight = Parameter(kernel_shape, dtype) if bias: self.bias = Parameter((self.out_channels,), dtype) else: self.bias = None def forward(self, x: Tensor) -> Tensor: # pylint: disable=invalid-name """ Forward method for conv3d layer. Parameters ---------- x : Tensor The input tensor. Returns ------- ret : Tensor The output tensor for the conv3d layer. """ return op.conv3d( x, self.weight, self.bias, self.stride, self.padding, self.dilation, self.groups, self.data_layout, ) class ConvTranspose1D(Module): """Applies a 1D transposed convolution (fractionally-strided convolution). Parameters ---------- in_channels : int Number of channels in the input. out_channels : int Number of channels produced by the transposed convolution. kernel_size : int Size of the convolving kernel. stride : int Stride of the convolution. Default: 1. padding : int Zero-padding added to both sides of the input. Default: 0. output_padding : int Additional size added to one side of the output shape. Default: 0. dilation : int Spacing between kernel elements. Default: 1. groups : int Number of blocked connections from input to output channels. Default: 1. bias : bool If ``True``, adds a learnable bias. Default: ``True``. dtype : Optional[str] Data type for weight and bias. ``None`` uses the default dtype. """ def __init__( self, in_channels: int, out_channels: int, kernel_size: int, stride: int = 1, padding: int = 0, output_padding: int = 0, dilation: int = 1, groups: int = 1, bias: bool = True, dtype: str | None = None, ) -> None: super().__init__() self.in_channels = in_channels self.out_channels = out_channels self.kernel_size = kernel_size self.stride = stride self.padding = padding self.output_padding = output_padding self.dilation = dilation self.groups = groups self.weight = Parameter( ( self.in_channels, int(self.out_channels // self.groups), self.kernel_size, ), dtype, ) if bias: self.bias = Parameter((self.out_channels,), dtype) else: self.bias = None def forward(self, x: Tensor) -> Tensor: """ Forward method for conv transpose 1d layer. Parameters ---------- x : Tensor The input tensor. Returns ------- ret : Tensor The output tensor for the conv transpose 1d layer. """ return op.conv1d_transpose( x, self.weight, self.bias, self.stride, self.padding, self.output_padding, self.dilation, self.groups, ) class LayerNorm(Module): """Applies Layer Normalization over the last dimension. Parameters ---------- normalized_shape : int Size of the last dimension to normalize over. eps : Optional[float] Value added to the denominator for numerical stability. Default: ``1e-5``. elementwise_affine : bool If ``True``, learnable affine parameters (weight and bias) are added. Default: ``True``. dtype : Optional[str] Data type for the affine parameters. ``None`` uses the default dtype. """ def __init__( self, normalized_shape: int, eps: float | None = 1e-5, elementwise_affine: bool = True, dtype: str | None = None, ) -> None: super().__init__() self.normalized_shape = normalized_shape self.eps = eps self.elementwise_affine = elementwise_affine if self.elementwise_affine: self.weight = Parameter((normalized_shape,), dtype=dtype) self.bias = Parameter((normalized_shape,), dtype=dtype) else: self.weight = None self.bias = None def forward(self, x: Tensor) -> Tensor: """ Forward method for layer normalization layer. Parameters ---------- x : Tensor The input tensor. Returns ------- ret : Tensor The output tensor for the layer normalization layer. """ return op.layer_norm( x, normalized_shape=self.normalized_shape, weight=self.weight, bias=self.bias, eps=self.eps, ) class RMSNorm(Module): """Applies Root Mean Square Layer Normalization. Parameters ---------- hidden_size : int Size of the weight parameter. axes : Union[int, List[int]] The axes over which to compute the RMS norm. epsilon : float Value added to the denominator for numerical stability. Default: ``1e-5``. bias : bool If ``True``, adds a learnable bias after normalization. Default: ``True``. dtype : Optional[str] Data type for the parameters. ``None`` uses the default dtype. """ def __init__( self, hidden_size: int, axes: int | list[int], epsilon: float = 1e-5, bias: bool = True, dtype: str | None = None, ): super().__init__() self.epsilon = epsilon self.axes = axes self.weight = Parameter((hidden_size,), dtype=dtype) if bias: self.bias = Parameter((hidden_size,), dtype=dtype) else: self.bias = None def forward(self, x: Tensor): """ Forward method for rms norm layer. Parameters ---------- x : Tensor The input tensor. Returns ------- ret : Tensor The output tensor for the rms norm layer. """ out = op.rms_norm(x, weight=self.weight, axes=self.axes, epsilon=self.epsilon) if self.bias: out = op.add(out, self.bias) return out class GroupNorm(Module): """Applies Group Normalization. Parameters ---------- num_groups : int Number of groups to separate the channels into. num_channels : int Number of channels in the input, must be divisible by *num_groups*. eps : float Value added to the denominator for numerical stability. Default: ``1e-5``. affine : bool If ``True``, learnable per-channel affine parameters are added. Default: ``True``. dtype : Optional[str] Data type for the affine parameters. ``None`` uses the default dtype. """ def __init__( self, num_groups: int, num_channels: int, eps: float = 1e-5, affine: bool = True, dtype: str | None = None, ): super().__init__() self.num_groups = num_groups self.num_channels = num_channels self.eps = eps if affine: self.weight = Parameter((num_channels,), dtype=dtype) self.bias = Parameter((num_channels,), dtype=dtype) else: self.weight = None self.bias = None def forward(self, x: Tensor, channel_axis: int = 1, axes: list[int] | None = None): """ Forward method for group norm layer. Parameters ---------- x : Tensor The input tensor. channel_axis : int Channel axis of the input data. axes : Optional[List[int]] Optional list of axes to compute norm over, if not specified, assumes that the first two axes should be left alone. Returns ------- ret : Tensor The output tensor for the group norm layer. """ return op.group_norm( x, self.num_groups, self.weight, self.bias, self.eps, channel_axis, axes ) class KVCache(Effect): """Managed key-value cache for autoregressive decoding. ``KVCache`` is a TVM-specific ``Effect`` that allocates and maintains a runtime cache for storing past key/value tensors in transformer models. Unlike regular ``Module`` parameters, effects are registered with the Relax VM and carry mutable state across calls (append, reset) without being passed as explicit function arguments. The cache is pre-allocated with shape ``[init_seq_len, *unit_shape]`` and grows via the ``append`` method at runtime. Use ``init_seq_len`` to control the initial allocation size. Parameters ---------- init_seq_len : int Initial sequence-length capacity of the cache allocation. unit_shape : Sequence[int] Shape of a single cache entry excluding the sequence dimension. For multi-head attention this is typically ``[num_heads, head_dim]``. dtype : Optional[str] Data type of the cache tensor. ``None`` uses the default dtype. """ init_seq_len: int unit_shape: list[int] dtype: str cache: rx.Var | None def __init__( self, init_seq_len: int, unit_shape: Sequence[int], dtype: str | None = None, ): if dtype is None: dtype = get_default_dtype() # Usually the shape is: [init_seq_len, num_heads, head_dim] # and unit_shape = [num_heads, head_dim] self.init_seq_len = init_seq_len self.unit_shape = [int(i) for i in unit_shape] self.dtype = dtype def emit_init(self, name_hint: str, bb: rx.BlockBuilder): # pylint: disable=arguments-renamed """ Emit the initialization of the KVCache effect. Parameters ---------- name_hint : str The name hint of the initialization binding Var. bb : relax.BlockBuilder The relax BlockBuilder to emit. """ init_shape = rx.ShapeExpr([self.init_seq_len] + self.unit_shape) return [ bb.emit( rx.op.call_pure_packed( "vm.builtin.attention_kv_cache_create", rx.op.zeros(init_shape, self.dtype), init_shape, rx.prim_value(0), ty_args=rx.ObjectType(), ), name_hint=name_hint, ) ] def create(self, name_hint: str) -> list[rx.Var]: """ Create the implicit inputs to a relax.Function that represents the KVCache effect. Parameters ---------- name_hint : str The name hint of the relax.Var. Returns ------- ret : List[relax.Var] The relax.Var for KVCache. """ cache = rx.Var(name_hint, ty=rx.AnyType()) return [cache] def set_state(self, state_vars: list[rx.Var]) -> None: (self.cache,) = state_vars def finalize(self) -> list[rx.Var]: """ Finalize the KVCache effect as the implicit return value of a relax.Function. Returns ------- ret : List[rx.Var] The output relax.Var as KVCache. """ result = self.cache self.cache = None return [result] def to(self, dtype: str | None = None) -> None: """ Convert the KVCache effect to specific dtype. Parameters ---------- dtype : Optional[str] The target data type to convert. """ if dtype is not None: self.dtype = dtype def view(self, seq_len: tirx.Var) -> Tensor: """ View the last elements in KVCache. Parameters ---------- seq_len : tirx.Var The number of last elements to view. Returns ------- ret : Tensor The last tensor to view. """ shape = rx.ShapeExpr([seq_len] + self.unit_shape) return Tensor( _expr=rx.BlockBuilder.current().emit( rx.op.call_pure_packed( "vm.builtin.attention_kv_cache_view", self.cache, shape, ty_args=rx.TensorType(shape, self.dtype), ) ) ) def append(self, new_element: Tensor) -> None: """ Append a new element in KVCache. Parameters ---------- new_element : Tensor The new tensor to append. """ if new_element.dtype != self.dtype: raise TypeError( f'KVCache has been set to use dtype "{self.dtype}", but got "{new_element.dtype}"' ) self.cache = rx.BlockBuilder.current().emit( rx.op.call_inplace_packed( "vm.builtin.attention_kv_cache_append", self.cache, new_element._expr, inplace_indices=[0], ty_args=rx.AnyType(), ) ) class Embedding(Module): """A lookup table that retrieves embeddings by index. Parameters ---------- num : Union[int, str, tirx.Expr] Size of the embedding dictionary (vocabulary size). Can be symbolic. dim : Union[int, str, tirx.Expr] Size of each embedding vector. Can be symbolic. dtype : Optional[str] Data type of the embedding weight. ``None`` uses the default dtype. """ def __init__( self, num: int | str | tirx.Expr, dim: int | str | tirx.Expr, dtype: str | None = None, ): self.num = num self.dim = dim self.weight = Parameter((num, dim), dtype=dtype) def forward(self, x: Tensor): """ Forward method for embedding layer. Parameters ---------- x : Tensor The input tensor. Returns ------- ret : Tensor The output tensor for the embedding layer. """ if x.ndim == 1: return op.take(self.weight, x, axis=0) return op.reshape( op.take( self.weight, op.reshape(x, shape=[-1]), axis=0, ), shape=[*x.shape, self.weight.shape[1]], ) class TimestepEmbedding(Module): """MLP that projects timestep embeddings, following the HuggingFace diffusers convention. Consists of two linear layers with an activation in between, and an optional conditional projection and post-activation. Parameters ---------- in_channels : int Dimensionality of the input timestep embedding. time_embed_dim : int Dimensionality of the intermediate (hidden) projection. act_fn : str Activation function name. Currently only ``"silu"`` is supported. out_dim : Optional[int] Dimensionality of the output. If ``None``, defaults to *time_embed_dim*. post_act_fn : Optional[str] Optional post-activation applied after the second linear layer. cond_proj_dim : Optional[int] If set, adds a linear projection from a conditioning signal of this dimensionality to *in_channels*, which is added to the input sample. """ def __init__( self, in_channels: int, time_embed_dim: int, act_fn: str = "silu", out_dim: int | None = None, post_act_fn: str | None = None, cond_proj_dim: int | None = None, ): self.linear_1 = Linear(in_channels, time_embed_dim) if cond_proj_dim is not None: self.cond_proj = Linear(cond_proj_dim, in_channels, bias=False) else: self.cond_proj = None assert act_fn == "silu", "Only SiLU activations are supported." self.act = SiLU() if out_dim is not None: time_embed_dim_out = out_dim else: time_embed_dim_out = time_embed_dim self.linear_2 = Linear(time_embed_dim, time_embed_dim_out) if post_act_fn is None: self.post_act = None else: assert self.post_act == "silu", "Only SiLU post-activation supported." self.post_act = SiLU() def forward(self, sample: Tensor, condition: Tensor | None = None): """ Forward method for TimestepEmbedding layer. Parameters ---------- sample : Tensor The input timestep that should be looked up. condition : Optional[Tensor] Optional additional projection matrix. Returns ------- ret : Tensor The resulting embedding lookup for the input sample. """ if condition is not None: sample = sample + self.cond_proj(condition) sample = self.linear_1(sample) if self.act is not None: sample = self.act(sample) sample = self.linear_2(sample) if self.post_act is not None: sample = self.post_act(sample) return sample class Timesteps(Module): """Sinusoidal positional embedding for diffusion timesteps (HuggingFace convention). Parameters ---------- num_channels : int Dimensionality of the embedding (number of sinusoidal channels). flip_sin_to_cos : bool If ``True``, swap sin and cos components. Default: ``False``. downscale_freq_shift : float Shift applied to the frequency denominator. Default: ``1``. """ def __init__( self, num_channels: int, flip_sin_to_cos: bool = False, downscale_freq_shift: float = 1 ): self.num_channels = num_channels self.flip_sin_to_cos = flip_sin_to_cos self.downscale_freq_shift = downscale_freq_shift def forward(self, x: Tensor): return op.get_timestep_embedding( x, embedding_dim=self.num_channels, flip_sin_to_cos=self.flip_sin_to_cos, downscale_freq_shift=self.downscale_freq_shift, ) class Attention(Module): """ A cross attention layer. Parameters ---------- query_dim : int The number of channels in the query. cross_attention_dim : Optional[int] The number of channels in the encoder_hidden_states. If not given, defaults to `query_dim`. heads : int The number of heads to use for multi-head attention. dim_head : int The number of channels in each head. bias : bool Set to `True` for the query, key, and value linear layers to contain a bias parameter. norm_num_groups : Optional[int] When set, group norm is applied to the input using this number of groups. out_bias : bool Set to `True` to apply a bias to the output linear layer. scale_qk : bool Whether to apply scaling to query and key tensors. """ def __init__( self, query_dim: int, cross_attention_dim: int | None = None, heads: int = 8, dim_head: int = 64, bias: bool = False, norm_num_groups: int | None = None, out_bias: bool = True, scale_qk: bool = True, ): self.query_dim = query_dim self.cross_attention_dim = cross_attention_dim if cross_attention_dim else query_dim self.heads = heads self.dim_head = dim_head self.bias = bias self.norm_num_groups = norm_num_groups self.out_bias = out_bias self.scale_qk = scale_qk self.scale = dim_head**-0.5 if self.scale_qk else 1.0 self.inner_dim = dim_head * heads self.to_q = Linear(self.query_dim, self.inner_dim, bias=self.bias) self.to_k = Linear(self.cross_attention_dim, self.inner_dim, bias=self.bias) self.to_v = Linear(self.cross_attention_dim, self.inner_dim, bias=self.bias) if self.norm_num_groups is not None: self.group_norm = GroupNorm( num_channels=self.query_dim, num_groups=self.norm_num_groups, affine=True ) else: self.group_norm = None self.to_out = ModuleList([Linear(self.inner_dim, self.query_dim, bias=self.out_bias)]) def forward( self, hidden_states: Tensor, encoder_hidden_states: Tensor | None = None, attention_mask: Tensor | None = None, **cross_attention_kwargs, ): """ Forward method for Attention layer. Parameters ---------- hidden_states : Tensor The input sample tensor. encoder_hidden_states : Optional[Tensor] Previous hidden step hidden states. attention_mask : Optional[Tensor] Mask tensor for attention, currently not supported. Returns ------- ret : Tensor The output tensor for the embedding layer. """ # This implementation assumes use of torch 2.0 scaled_dot_product attention. assert attention_mask is None, "Attention mask not yet supported." if self.group_norm is not None: hidden_states = self.group_norm(hidden_states, channel_axis=2, axes=[1]) query = self.to_q(hidden_states) if encoder_hidden_states is None: encoder_hidden_states = hidden_states key = self.to_k(encoder_hidden_states) value = self.to_v(encoder_hidden_states) head_dim = int(self.inner_dim // self.heads) query = op.reshape(query, [0, -1, self.heads, head_dim]) key = op.reshape(key, [0, -1, self.heads, head_dim]) value = op.reshape(value, [0, -1, self.heads, head_dim]) hidden_states = op.scaled_dot_product_attention(query, key, value, is_causal=False) # Return to proper shape. hidden_states = op.reshape(hidden_states, (0, -1, self.heads * head_dim)) # Linear projection hidden_states = self.to_out[0](hidden_states) return hidden_states