runmat-runtime 0.4.1

Core runtime for RunMat with builtins, BLAS/LAPACK integration, and execution APIs
Documentation
{
  "title": "expm1",
  "category": "math/elementwise",
  "keywords": [
    "expm1",
    "exp(x)-1",
    "exponential",
    "elementwise",
    "gpu",
    "precision"
  ],
  "summary": "Accurate element-wise computation of exp(x) - 1 for scalars, vectors, matrices, or N-D tensors.",
  "references": [],
  "gpu_support": {
    "elementwise": true,
    "reduction": false,
    "precisions": [
      "f32",
      "f64"
    ],
    "broadcasting": "matlab",
    "notes": "Falls back to the host implementation when the active provider lacks unary_expm1."
  },
  "fusion": {
    "elementwise": true,
    "reduction": false,
    "max_inputs": 1,
    "constants": "inline"
  },
  "requires_feature": null,
  "tested": {
    "unit": "builtins::math::elementwise::expm1::tests",
    "integration": "builtins::math::elementwise::expm1::tests::expm1_gpu_provider_roundtrip"
  },
  "description": "`Y = expm1(X)` evaluates `exp(X) - 1` element-wise while maintaining high accuracy for values of `X` that are close to zero. It mirrors MATLAB semantics across scalars, vectors, matrices, logical arrays, character arrays, and complex inputs.",
  "behaviors": [
    "Logical inputs are promoted to double precision (`true -> 1.0`, `false -> 0.0`) before execution.",
    "Character arrays are interpreted as their numeric code points and return dense double tensors.",
    "Complex values follow MATLAB's definition by computing `exp(z) - 1` using complex arithmetic.",
    "Existing GPU tensors remain on the device when the registered provider implements `unary_expm1`; otherwise RunMat gathers the data, computes on the CPU, and returns the result."
  ],
  "examples": [
    {
      "description": "Maintaining precision for tiny growth rates",
      "input": "x = 1e-12;\ny = expm1(x)",
      "output": "y = 1.0000000000005e-12"
    },
    {
      "description": "Applying expm1 to model percentage growth",
      "input": "rates = [-0.10 -0.05 0 0.05 0.10];\nfactors = expm1(rates)",
      "output": "factors = [-0.0952 -0.0488 0 0.0513 0.1052]"
    },
    {
      "description": "Running expm1 on GPU arrays",
      "input": "G = gpuArray(linspace(-1, 1, 5));\nresult = expm1(G);\nout = gather(result)",
      "output": "out = [-0.6321 -0.3935 0 0.6487 1.7183]"
    },
    {
      "description": "Using expm1 with complex numbers",
      "input": "z = [1+1i, -1+pi*1i];\nw = expm1(z)",
      "output": "w = [0.4687 + 2.2874i, -1.3679 + 0.0000i]"
    },
    {
      "description": "Applying expm1 to character data",
      "input": "C = 'ABC';\nY = expm1(C)",
      "output": "Y = [1.6949e+28 4.6072e+28 1.2524e+29]"
    }
  ],
  "faqs": [
    {
      "question": "When should I prefer `expm1` over `exp(x) - 1`?",
      "answer": "Use `expm1` whenever `x` can be very close to zero. It avoids catastrophic cancellation and matches MATLAB's high-accuracy results for tiny magnitudes."
    },
    {
      "question": "Does `expm1` change my tensor's shape?",
      "answer": "No. The output has the same shape as the input, subject to MATLAB broadcasting semantics."
    },
    {
      "question": "How are logical arrays handled?",
      "answer": "Logical values convert to doubles before applying `expm1`, so `expm1([true false])` yields a double array `[e - 1, 0]`."
    },
    {
      "question": "What about complex inputs?",
      "answer": "Complex scalars and tensors use MATLAB's complex exponential formula and subtract one from the result, keeping both real and imaginary parts accurate."
    },
    {
      "question": "What happens if the GPU provider lacks `unary_expm1`?",
      "answer": "RunMat gathers the tensor to the host, computes with double precision, and returns the correct result. Subsequent fused kernels still see accurate residency metadata."
    },
    {
      "question": "Can I expect double precision?",
      "answer": "Yes. RunMat stores dense numeric tensors in double precision (`f64`). GPU providers may choose single precision internally but convert back to double when returning data to the runtime."
    },
    {
      "question": "How does `expm1` interact with fusion?",
      "answer": "The fusion planner recognises `expm1` as an element-wise op. Providers that support fused kernels can materialise `expm1` directly in generated WGSL."
    }
  ],
  "links": [
    {
      "label": "exp",
      "url": "./exp"
    },
    {
      "label": "log1p",
      "url": "./log1p"
    },
    {
      "label": "sin",
      "url": "./sin"
    },
    {
      "label": "gpuArray",
      "url": "./gpuarray"
    },
    {
      "label": "gather",
      "url": "./gather"
    },
    {
      "label": "abs",
      "url": "./abs"
    },
    {
      "label": "angle",
      "url": "./angle"
    },
    {
      "label": "conj",
      "url": "./conj"
    },
    {
      "label": "double",
      "url": "./double"
    },
    {
      "label": "factorial",
      "url": "./factorial"
    },
    {
      "label": "gamma",
      "url": "./gamma"
    },
    {
      "label": "hypot",
      "url": "./hypot"
    },
    {
      "label": "imag",
      "url": "./imag"
    },
    {
      "label": "ldivide",
      "url": "./ldivide"
    },
    {
      "label": "log",
      "url": "./log"
    },
    {
      "label": "log10",
      "url": "./log10"
    },
    {
      "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": "sqrt",
      "url": "./sqrt"
    },
    {
      "label": "times",
      "url": "./times"
    }
  ],
  "source": {
    "label": "`crates/runmat-runtime/src/builtins/math/elementwise/expm1.rs`",
    "url": "https://github.com/runmat-org/runmat/blob/main/crates/runmat-runtime/src/builtins/math/elementwise/expm1.rs"
  },
  "gpu_residency": "In most workflows you do **not** need to call `gpuArray` manually. RunMat's auto-offload planner and fusion engine keep data on the GPU when beneficial. You can still call `gpuArray` to mirror MathWorks MATLAB workflows or to pin data on the device explicitly.",
  "gpu_behavior": [
    "RunMat Accelerate keeps tensors resident on the GPU whenever the provider exposes the `unary_expm1` hook. When the hook is missing or errors, RunMat automatically gathers the tensor, performs the computation on the host using `f64::expm1` for the real components, and returns the result while preserving residency metadata. This ensures users always observe MATLAB-compatible behaviour without manual residency management."
  ]
}