{"record":{"id":"ef7b05603b17faed","repo":"google-research/timesfm","slug":"output-dims-must-be-a-multiple-of-4-config-outpu","errorCode":null,"errorMessage":"Output dims must be a multiple of 4: {config.output_dims} % 4 != 0.","messagePattern":"Output dims must be a multiple of 4: (.+?) % 4 != 0\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/timesfm/flax/dense.py","lineNumber":81,"sourceCode":"    else:\n      raise ValueError(f\"Activation: {config.activation} not supported.\")\n\n  def __call__(self, x: Float[Array, \"b ... i\"]) -> Float[Array, \"b ... o\"]:\n    return self.output_layer(\n      self.activation(self.hidden_layer(x))\n    ) + self.residual_layer(x)\n\n\nclass RandomFourierFeatures(nnx.Module):\n  \"\"\"Random Fourier features layer.\"\"\"\n\n  __data__ = (\"phrase_shifts\",)\n\n  def __init__(self, config: RandomFourierFeaturesConfig, *, rngs=nnx.Rngs(42)):\n    self.config = config\n\n    if config.output_dims % 4 != 0:\n      raise ValueError(\n        f\"Output dims must be a multiple of 4: {config.output_dims} % 4 != 0.\"\n      )\n    num_projected_features = config.output_dims // 4\n\n    self.phase_shifts = nnx.Param(jnp.zeros(shape=(2, num_projected_features)))\n    self.projection_layer = nnx.Linear(\n      in_features=config.input_dims,\n      out_features=num_projected_features,\n      use_bias=config.use_bias,\n      rngs=rngs,\n    )\n    self.residual_layer = nnx.Linear(\n      in_features=config.input_dims,\n      out_features=config.output_dims,\n      use_bias=config.use_bias,\n      rngs=rngs,\n    )\n","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/google-research/timesfm/blob/331c6d33cb1ac2611de3056d0ac7164aab6301eb/src/timesfm/flax/dense.py#L63-L99","documentation":"RandomFourierFeatures projection splits output_dims into 4 components, so output_dims must be divisible by 4; num_projected_features is computed as output_dims // 4. The constructor validates this upfront and raises ValueError otherwise.","triggerScenarios":"Creating a RandomFourierFeaturesConfig with output_dims such as 6, 10, 25, 100 (any non-multiple of 4) and passing it to the module's __init__.","commonSituations":"Choosing an arbitrary embedding size for random Fourier features (e.g. 100), copying dims from another model's hidden size, or incrementing dims by small amounts without checking divisibility.","solutions":["Set config.output_dims to the nearest multiple of 4 that fits your needs (e.g. 100 -> 100 is invalid, use 100 -> 96 or 104).","Derive output_dims programmatically: output_dims = desired_features * 4.","If the value comes from a checkpoint/model card, use the exact documented dims."],"exampleFix":"// before\nconfig = RandomFourierFeaturesConfig(output_dims=100)  # ValueError\n// after\nconfig = RandomFourierFeaturesConfig(output_dims=104)  # 104 % 4 == 0","handlingStrategy":"validation","validationCode":"if config.output_dims % 4 != 0:\n    raise ValueError(f\"output_dims must be a multiple of 4, got {config.output_dims}\")","typeGuard":null,"tryCatchPattern":"try:\n    rff = RandomFourierFeatures(config)\nexcept ValueError as e:\n    if \"% 4\" in str(e):\n        config.output_dims = ((config.output_dims // 4) + 1) * 4\n        rff = RandomFourierFeatures(config)\n    else:\n        raise","preventionTips":["Pick output_dims as num_features * 4 by construction.","Validate dims at config-load time, not module-construction time.","Encode the constraint in the config dataclass via a __post_init__ check."],"tags":["config","validation","shape","valueerror"],"backgroundTag":"dimension-not-divisible","analyzedSha":"331c6d33cb1ac2611de3056d0ac7164aab6301eb","analyzedAt":"2026-08-29T01:04:23.138Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}