id: vector_field_similarity
name: Vector Field Similarity
description: >
  Per-pixel similarity between a multi-band "vector field" raster (a
  raster where each pixel holds a D-dimensional vector spread across D
  bands) and a reference vector. Generalizes the original
  `embedding_trajectory_similarity` op — the math (cosine / L2 over
  per-pixel vectors) is the same regardless of whether the per-pixel
  vector is a stacked time trajectory, a single-time embedding, an
  ensemble of FM embeddings, or any other multi-band feature stack.

  Typical inputs:
    - Output of `stack_concat` (T*D bands; trajectory recipe).
    - A single-time embedding raster (D bands).
    - Any multi-band feature stack where bands form a coherent vector.

  The reference can be specified two ways:
    1. A single pixel index via the `reference_pixel` param
       (object `{row, col}`).
    2. A region mask passed as the `reference_mask` input (single-band
       raster matching the field grid; cells with finite value > 0
       are included). The reference vector is the NaN-aware mean of
       per-pixel vectors under the mask.

  Default metric is cosine similarity in [-1, 1]. Setting `metric: "l2"`
  yields L2 distance in [0, +inf).

  Multi-reference output: `references` (optional, array of
  `{row, col}` pixels OR an array of `reference_mask` keys) emits one
  output band per reference. K references → K bands; tile previewers
  expecting <=4 bands may fail.

  NaN handling: pixels whose vector contains any NaN return NaN. If the
  reference itself contains NaN (or the mask selects no usable cells),
  the entire output is NaN.

  Renamed 2026-05-01 from `embedding_trajectory_similarity` per
  arch-review verdict that the math is general — see registry alias
  in `packages/compute/src/ops/index.ts` for backward compatibility.
version: 1.0.0
category: analysis
type: raster-to-raster

inputs:
  - name: field
    type: raster
    description: >
      Multi-band raster whose bands form a per-pixel D-dimensional
      vector. Common producers: `stack_concat` (T*D bands; trajectory
      pattern), `eofm_embed` (D bands; single-time embedding).
      Aliased as `trajectory` for backward compatibility with the
      original `embedding_trajectory_similarity` API.
    required: true
  - name: reference_mask
    type: raster
    description: >
      Optional single-band mask raster (same grid as `field`). If
      provided, cells with finite value > 0 define the reference region
      and the reference vector is the NaN-aware mean across them.
      Mutually exclusive with `reference_pixel` param.
    required: false

# Structural: cosine / L2 similarity on any multi-band per-pixel vector
# field (trajectory, embedding, ensemble). requires: omitted; similarity
# score is recipe-specific, no canonical concept slug.

outputs:
  - name: similarity
    type: raster
    format: cog
    dtype: float32
    description: >
      Per-pixel similarity scores. For cosine, values in [-1, 1]
      (1 = identical vector). For L2, values in [0, +inf) (0 =
      identical). NaN at pixels with NaN in their vector. When
      multiple references are supplied, output is a multi-band raster
      with one band per reference (band name = `similarity_{ref_id}`).

params:
  reference_pixel:
    type: object
    description: >
      Single-pixel reference index `{row, col}` (zero-indexed). Mutually
      exclusive with the `reference_mask` input. Either this param or
      the mask must be supplied.
    required: false
  metric:
    type: enum
    enum: [cosine, l2]
    default: cosine
    description: >
      Similarity metric. `cosine` returns values in [-1, 1] (higher =
      more similar). `l2` returns Euclidean distance in [0, +inf)
      (lower = more similar).

# backends: audited 2026-08-14 (defect 50). A key means a runtime that DISPATCHES this op —
# folia-engine `dispatch_op` (products/sdk/folia-engine/src/lib.rs), a `registerOp`/OP_TABLE
# entry in packages/compute, `_BUILTIN_OP_MAP` in folia/compute.py, or a backend manifest
# (folia/backends/*/backend.yaml).
backends:
  js:
    function: vector_field_similarity

display_hints:
  map:
    renderer: maplibre
    type: raster
    palette: viridis
    range: [-1.0, 1.0]
    # WARNING: With multiple references (K > 1), output has K bands —
    # one per reference centroid (e.g., `similarity_archetype_0` …
    # `similarity_archetype_K-1`). Tile previewers and renderers that
    # assume <=4 bands will fail or render incorrectly. Downstream
    # callers should reduce (argmax / softmax) to a single-band layer
    # before display, or render per-band as separate tile sets.
    multi_band_warning: true
  chart:
    renderer: observable-plot
    chartType: histogram
    x: similarity
  info:
    fields: [reference_kind, metric, n_references, n_valid_pixels]
    section: Vector Field Operations
