runmat-runtime 0.4.1

Core runtime for RunMat with builtins, BLAS/LAPACK integration, and execution APIs
Documentation
{
  "title": "hypot",
  "category": "math/elementwise",
  "keywords": [
    "hypot",
    "euclidean norm",
    "distance",
    "gpu",
    "elementwise"
  ],
  "summary": "Element-wise Euclidean norm sqrt(x.^2 + y.^2) with MATLAB-compatible broadcasting.",
  "references": [
    "https://www.mathworks.com/help/matlab/ref/hypot.html"
  ],
  "gpu_support": {
    "elementwise": true,
    "reduction": false,
    "precisions": [
      "f32",
      "f64"
    ],
    "broadcasting": "matlab",
    "notes": "Runs entirely on the GPU when the provider implements elem_hypot; otherwise the runtime gathers inputs and falls back to the CPU path."
  },
  "fusion": {
    "elementwise": true,
    "reduction": false,
    "max_inputs": 2,
    "constants": "inline"
  },
  "requires_feature": null,
  "tested": {
    "unit": "builtins::math::elementwise::hypot::tests",
    "integration": "builtins::math::elementwise::hypot::tests::hypot_gpu_provider_roundtrip",
    "gpu": "builtins::math::elementwise::hypot::tests::hypot_wgpu_matches_cpu_elementwise"
  },
  "description": "`z = hypot(x, y)` computes the element-wise Euclidean norm `sqrt(|x|.^2 + |y|.^2)` with overflow-resistant arithmetic. It accepts scalars, vectors, matrices, N-D tensors, and complex inputs, following MATLAB's implicit expansion (broadcasting) rules.",
  "behaviors": [
    "Real inputs return non-negative doubles even when the operands are negative.",
    "Complex arguments are converted to their magnitudes before forming `sqrt(|x|.^2 + |y|.^2)`, matching MATLAB's definition since R2020b.",
    "Scalars broadcast across the other operand when shapes are compatible; otherwise an error is raised.",
    "Empty inputs propagate emptiness according to MATLAB's size rules.",
    "`Inf` operands return `Inf`; `NaN` operands propagate `NaN`."
  ],
  "examples": [
    {
      "description": "Computing the hypotenuse of two scalars",
      "input": "result = hypot(3, 4)",
      "output": "result = 5"
    },
    {
      "description": "Using `hypot` with vectors and implicit expansion",
      "input": "x = [-3, 0, 4];\ny = 4;\nnorms = hypot(x, y)",
      "output": "norms = [5 4 5]"
    },
    {
      "description": "Calculating per-element distances between two matrices",
      "input": "X = [1 2; 3 4];\nY = [0 1; 1 0];\ndist = hypot(X, Y)",
      "output": "dist = [1.0000 2.2361; 3.1623 4.0000]"
    },
    {
      "description": "Working with complex numbers",
      "input": "a = [1+2i, 3-4i];\nb = [2-1i, -1+1i];\nmag = hypot(a, b)",
      "output": "mag = [3.1623 5.1962]"
    },
    {
      "description": "Applying `hypot` to character data",
      "input": "codes = hypot('A', zeros(1, 1))",
      "output": "codes = 65.0000"
    },
    {
      "description": "Executing `hypot` on GPU arrays",
      "input": "Gx = gpuArray([3 4; 5 12]);\nGy = gpuArray([4 3; 12 5]);\ndistance_gpu = hypot(Gx, Gy);\ndistance = gather(distance_gpu)",
      "output": "distance = [5 5; 13 13]"
    }
  ],
  "faqs": [
    {
      "question": "Does `hypot` overflow for large inputs?",
      "answer": "No. Like MATLAB, RunMat uses a stable algorithm that scales the operands to avoid overflow and underflow. Very large inputs return finite results when mathematically possible."
    },
    {
      "question": "What happens when the inputs are negative?",
      "answer": "`hypot` always returns a non-negative result because it squares the magnitudes first. Signs on the inputs only matter when NaN or Inf is involved."
    },
    {
      "question": "Can I mix real and complex inputs?",
      "answer": "Yes. Complex inputs are converted to magnitudes before forming the norm, so you can freely combine real, complex, and logical data."
    },
    {
      "question": "Does `hypot` accept logical or character arrays?",
      "answer": "Yes. Logical values map to `0` and `1`, while character arrays use their Unicode code points (as doubles), matching MATLAB semantics."
    },
    {
      "question": "How does broadcasting work?",
      "answer": "The function applies MATLAB's implicit expansion: singleton dimensions expand to match the other operand. If a non-singleton dimension differs, RunMat raises a dimension mismatch error."
    },
    {
      "question": "Are GPU results identical to CPU results?",
      "answer": "For double precision providers the results match bit-for-bit. Single precision providers (e.g., `wgpu` in F32 mode) may differ by typical floating-point rounding."
    },
    {
      "question": "What if only one operand lives on the GPU?",
      "answer": "The runtime gathers the GPU operand, computes the norm on the host, and continues execution. Future providers may implement scalar expansion directly on the device."
    },
    {
      "question": "Does `hypot` allocate a new array?",
      "answer": "Yes. The builtin returns a fresh tensor. Fusion may elide intermediate buffers when the expression participates in a larger GPU kernel."
    },
    {
      "question": "How can I compute the magnitude of a vector of components?",
      "answer": "Use `hypot` repeatedly: `hypot(x, hypot(y, z))` computes the Euclidean norm of `(x, y, z)` element-wise without manual squaring."
    }
  ],
  "links": [
    {
      "label": "sqrt",
      "url": "./sqrt"
    },
    {
      "label": "abs",
      "url": "./abs"
    },
    {
      "label": "sin",
      "url": "./sin"
    },
    {
      "label": "gpuArray",
      "url": "./gpuarray"
    },
    {
      "label": "gather",
      "url": "./gather"
    },
    {
      "label": "angle",
      "url": "./angle"
    },
    {
      "label": "conj",
      "url": "./conj"
    },
    {
      "label": "double",
      "url": "./double"
    },
    {
      "label": "exp",
      "url": "./exp"
    },
    {
      "label": "expm1",
      "url": "./expm1"
    },
    {
      "label": "factorial",
      "url": "./factorial"
    },
    {
      "label": "gamma",
      "url": "./gamma"
    },
    {
      "label": "imag",
      "url": "./imag"
    },
    {
      "label": "ldivide",
      "url": "./ldivide"
    },
    {
      "label": "log",
      "url": "./log"
    },
    {
      "label": "log10",
      "url": "./log10"
    },
    {
      "label": "log1p",
      "url": "./log1p"
    },
    {
      "label": "log2",
      "url": "./log2"
    },
    {
      "label": "minus",
      "url": "./minus"
    },
    {
      "label": "plus",
      "url": "./plus"
    },
    {
      "label": "pow2",
      "url": "./pow2"
    },
    {
      "label": "power",
      "url": "./power"
    },
    {
      "label": "rdivide",
      "url": "./rdivide"
    },
    {
      "label": "real",
      "url": "./real"
    },
    {
      "label": "sign",
      "url": "./sign"
    },
    {
      "label": "single",
      "url": "./single"
    },
    {
      "label": "times",
      "url": "./times"
    }
  ],
  "source": {
    "label": "`crates/runmat-runtime/src/builtins/math/elementwise/hypot.rs`",
    "url": "https://github.com/runmat-org/runmat/blob/main/crates/runmat-runtime/src/builtins/math/elementwise/hypot.rs"
  },
  "gpu_residency": "Manual `gpuArray` calls are typically unnecessary. RunMat's planner keeps tensors on the GPU when profitable and automatically gathers data when the active provider lacks `elem_hypot`. You can still force residency with explicit `gpuArray`/`gather` to mirror MATLAB workflows or when interoperating with custom kernels.",
  "gpu_behavior": [
    "**Hook available and shapes match:** the Euclidean norm executes in a single fused GPU kernel.",
    "**Hook missing or implicit expansion required:** RunMat gathers the data to the host, computes the MATLAB-compatible result, and reuses the host tensor downstream.",
    "**Fusion-enabled expressions:** the fusion planner can emit a WGSL `hypot(a, b)` node so long as the active provider marks the group as supported; otherwise the execution seamlessly falls back to the host path.\n\nProviders can implement `elem_hypot` by emitting a single WGSL/compute shader that calls `hypot(a, b)`; the included WGPU backend demonstrates this pattern."
  ]
}