import math
from typing import Any, Optional
import mlx.core as mx
from mlx.nn.layers.base import Module
from mlx.nn.layers.quantized import QQLinear, QuantizedLinear
class Identity(Module):
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__()
def __call__(self, x: mx.array) -> mx.array:
return x
class Linear(Module):
def __init__(self, input_dims: int, output_dims: int, bias: bool = True) -> None:
super().__init__()
scale = math.sqrt(1.0 / input_dims)
self.weight = mx.random.uniform(
low=-scale,
high=scale,
shape=(output_dims, input_dims),
)
if bias:
self.bias = mx.random.uniform(
low=-scale,
high=scale,
shape=(output_dims,),
)
def _extra_repr(self) -> str:
return f"input_dims={self.weight.shape[1]}, output_dims={self.weight.shape[0]}, bias={'bias' in self}"
def __call__(self, x: mx.array) -> mx.array:
if "bias" in self:
x = mx.addmm(self["bias"], x, self["weight"].T)
else:
x = x @ self["weight"].T
return x
def to_quantized(
self,
group_size: Optional[int] = None,
bits: Optional[int] = None,
mode: str = "affine",
quantize_input: bool = False,
):
if quantize_input:
if mode not in ["nvfp4", "mxfp8"]:
raise ValueError(
f"Quantized activations are only supported for 'nvfp4' and 'mxfp8' modes, got {mode}."
)
return QQLinear.from_linear(self, group_size, bits, mode)
return QuantizedLinear.from_linear(self, group_size, bits, mode)
class Bilinear(Module):
def __init__(
self, input1_dims: int, input2_dims: int, output_dims: int, bias: bool = True
) -> None:
super().__init__()
scale = math.sqrt(1.0 / input1_dims)
self.weight = mx.random.uniform(
low=-scale,
high=scale,
shape=(output_dims, input2_dims, input1_dims),
)
if bias:
self.bias = mx.random.uniform(
low=-scale,
high=scale,
shape=(output_dims,),
)
def _extra_repr(self) -> str:
out, in2, in1 = self.weight.shape
return (
f"input1_dims={in1}, input2_dims={in2}, output_dims={out}, "
f"bias={'bias' in self}"
)
def __call__(self, x1: mx.array, x2: mx.array) -> mx.array:
out, in2, in1 = self.weight.shape
xshape = x1.shape[:-1]
x1 = x1.reshape(-1, in1)
x2 = x2.reshape(-1, 1, in2)
w = self.weight.reshape(out * in2, in1)
y = x1 @ w.T
y = y.reshape(-1, out, in2).swapaxes(-2, -1)
y = x2 @ y
y = y.squeeze(1)
y = y.reshape(*xshape, out)
if "bias" in self:
y = y + self.bias
return y