{"record":{"id":"fd0f4f5e612122d6","repo":"google-research/timesfm","slug":"inputs-must-be-of-rank-3-or-4-fd0f4f","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/torch/transformer.py","lineNumber":103,"sourceCode":"      / self.embedding_dims\n    )\n    timescale = (\n      self.min_timescale * (self.max_timescale / self.min_timescale) ** fraction\n    ).to(inputs.device)\n    if position is None:\n      seq_length = inputs.shape[1]\n      position = torch.arange(seq_length, dtype=torch.float32, device=inputs.device)[\n        None, :\n      ]\n\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\n    sinusoid_inp = position / timescale\n    sin = torch.sin(sinusoid_inp)\n    cos = torch.cos(sinusoid_inp)\n    first_half, second_half = torch.chunk(inputs, 2, dim=-1)\n    first_part = first_half * cos - second_half * sin\n    second_part = second_half * cos + first_half * sin\n    return torch.cat([first_part, second_part], dim=-1)\n\n\ndef _dot_product_attention(\n  query,\n  key,\n  value,\n  mask=None,\n):\n  \"\"\"Computes dot-product attention given query, key, and value.\"\"\"\n  attn_weights = torch.einsum(\"...qhd,...khd->...hqk\", query, key)","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/google-research/timesfm/blob/331c6d33cb1ac2611de3056d0ac7164aab6301eb/src/timesfm/torch/transformer.py#L85-L121","documentation":"The sinusoid position embedding supports 4-D inputs (batch, heads, seq, dim) and 3-D inputs (batch, seq, dim); any other rank has ambiguous broadcasting semantics for position and timescale, so forward() raises ValueError. Rank determines how position/timescale are broadcast.","triggerScenarios":"Calling the position-embedding forward with a tensor of ndim other than 3 or 4 — e.g. a batch-less (seq, dim) 2-D tensor, a flattened 1-D tensor, or a 5-D tensor from an extra leading dimension.","commonSituations":"Unit-testing the embedding module with a hand-made tensor lacking a batch dim; squeezing a batch of size 1 before the layer; stacking an extra ensemble/data axis upstream.","solutions":["Ensure inputs is 3-D (batch, seq, dim) or 4-D (batch, heads, seq, dim).","Add a batch dim if missing: inputs = inputs[None, ...].","Remove unintended leading dims: inputs = inputs.squeeze(0) or flatten extra axes.","Pass a position tensor whose shape broadcasts against the input rank."],"exampleFix":"// before\npos = sinusoid_position_embedding(inputs)  # inputs is (seq, dim), rank 2\n// after\npos = sinusoid_position_embedding(inputs[None, ...])  # add batch dim -> rank 3","handlingStrategy":"validation","validationCode":"if inputs.ndim not in (3, 4):\n    if inputs.ndim == 2:\n        inputs = inputs[None, ...]  # add batch dim\n    else:\n        raise ValueError(f\"expected rank 3 or 4, got {inputs.ndim}\")","typeGuard":"def is_embedding_input(x) -> bool:\n    return x.ndim in (3, 4)","tryCatchPattern":"try:\n    out = pos_emb(inputs)\nexcept ValueError:\n    inputs = inputs[None, ...]  # add missing batch dim\n    out = pos_emb(inputs)","preventionTips":["Never squeeze the batch dimension before position embeddings.","Keep tensors at rank 3 (b,t,d) or 4 (b,h,t,d) through the embedding.","Squeeze extra dims only after the embedding layer.","Add ndim asserts in custom call paths."],"tags":["value-error","shape-mismatch","transformer","torch"],"backgroundTag":"tensor-rank-mismatch","analyzedSha":"331c6d33cb1ac2611de3056d0ac7164aab6301eb","analyzedAt":"2026-08-29T01:04:23.138Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}