{"record":{"id":"58958ec9930a9526","repo":"google-research/timesfm","slug":"inputs-must-be-of-rank-3-or-4","errorCode":null,"errorMessage":"Inputs must be of rank 3 or 4.","messagePattern":"Inputs must be of rank 3 or 4\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/timesfm/flax/transformer.py","lineNumber":106,"sourceCode":"        \"The embedding dims of the rotary position embedding\"\n        \"must match the hidden dimension of the inputs.\"\n      )\n    half_embedding_dim = self.embedding_dims // 2\n    fraction = 2 * jnp.arange(0, half_embedding_dim) / self.embedding_dims\n    timescale = (\n      self.min_timescale * (self.max_timescale / self.min_timescale) ** fraction\n    )\n    if position is None:\n      seq_length = inputs.shape[1]\n      position = jnp.arange(seq_length, dtype=jnp.float32)[None, :]\n    if len(inputs.shape) == 4:\n      position = position[..., None, None]\n      timescale = timescale[None, None, None, :]\n    elif len(inputs.shape) == 3:\n      position = position[..., None]\n      timescale = timescale[None, None, :]\n    else:\n      raise ValueError(\"Inputs must be of rank 3 or 4.\")\n    sinusoid_inp = position / timescale\n    sin = jnp.sin(sinusoid_inp)\n    cos = jnp.cos(sinusoid_inp)\n    first_half, second_half = jnp.split(inputs, 2, axis=-1)\n    first_part = first_half * cos - second_half * sin\n    second_part = second_half * cos + first_half * sin\n    first_part = first_part.astype(None)\n    second_part = second_part.astype(None)\n    return jnp.concatenate([first_part, second_part], axis=-1)\n\n\nclass PerDimScale(nnx.Module):\n  \"\"\"Per-dimension scaling.\"\"\"\n\n  __data__ = (\"per_dim_scale\",)\n\n  def __init__(self, num_dims: int, *, rngs=nnx.Rngs(42)):\n    del rngs","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/google-research/timesfm/blob/331c6d33cb1ac2611de3056d0ac7164aab6301eb/src/timesfm/flax/transformer.py#L88-L124","documentation":"The sinusoid broadcasting logic in the rotary embedding supports only rank-3 (b, n, d) and rank-4 (b, h, n, d) inputs, adjusting position/timescale axes accordingly. Any other rank falls through to this ValueError at forward time.","triggerScenarios":"Calling the rotary position embedding __call__ with a 2D tensor (no batch), a 1D vector, or a 5D tensor, e.g. passing a single sequence of shape (n, d) without a batch axis.","commonSituations":"Debugging with a single example and forgetting the batch dimension, squeezing the batch axis before the rotary layer, or piping in a tensor from another module that adds/removes an axis.","solutions":["Add a batch axis: reshape inputs from (n, d) to (1, n, d) before the call.","Ensure the tensor is exactly rank 3 or 4 (batch, [heads,] positions, features) when entering the module.","Check upstream code for unintended squeeze()/reshape() calls that drop or add axes."],"exampleFix":"// before\ny = rotary(x)  # x.shape == (n, d) -> ValueError\n// after\nx = x[None, :, :]  # now rank 3: (1, n, d)\ny = rotary(x)","handlingStrategy":"validation","validationCode":"if inputs.ndim not in (3, 4):\n    raise ValueError(f\"expected rank 3 or 4 input, got rank {inputs.ndim}\")","typeGuard":"def is_rank_3_or_4(x) -> bool:\n    return x.ndim in (3, 4)","tryCatchPattern":"try:\n    out = rotary(x)\nexcept ValueError as e:\n    if \"rank 3 or 4\" in str(e):\n        while x.ndim < 3:\n            x = x[None]\n        out = rotary(x)\n    else:\n        raise","preventionTips":["Never drop the batch axis before positional-embedding layers; use x[None] instead of squeeze().","Log/check tensor ranks at pipeline boundaries.","Write a smoke test running a rank-3 dummy tensor through the model."],"tags":["shape","validation","jax","tensor-rank"],"backgroundTag":"invalid-input-rank","analyzedSha":"331c6d33cb1ac2611de3056d0ac7164aab6301eb","analyzedAt":"2026-08-29T01:04:23.138Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}