runmat-runtime 0.4.1

Core runtime for RunMat with builtins, BLAS/LAPACK integration, and execution APIs
Documentation
{
  "title": "plus",
  "category": "math/elementwise",
  "keywords": [
    "plus",
    "element-wise addition",
    "+",
    "gpu",
    "implicit expansion"
  ],
  "summary": "Element-wise addition A + B with MATLAB-compatible implicit expansion, complex support, and GPU fallbacks.",
  "references": [
    "https://www.mathworks.com/help/matlab/ref/plus.html"
  ],
  "gpu_support": {
    "elementwise": true,
    "reduction": false,
    "precisions": [
      "f32",
      "f64"
    ],
    "broadcasting": "matlab",
    "notes": "Uses provider elem_add when both operands share a shape, scalar_add when exactly one operand is a scalar, and gathers to host for implicit expansion or unsupported operand types."
  },
  "fusion": {
    "elementwise": true,
    "reduction": false,
    "max_inputs": 2,
    "constants": "inline"
  },
  "requires_feature": null,
  "tested": {
    "unit": "builtins::math::elementwise::plus::tests::plus_scalar_numbers",
    "integration": "builtins::math::elementwise::plus::tests::plus_row_column_broadcast",
    "gpu": "builtins::math::elementwise::plus::tests::plus_gpu_pair_roundtrip",
    "wgpu": "builtins::math::elementwise::plus::tests::plus_wgpu_matches_cpu_elementwise",
    "like_gpu": "builtins::math::elementwise::plus::tests::plus_like_gpu_prototype_keeps_residency",
    "like_host": "builtins::math::elementwise::plus::tests::plus_like_host_gathers_gpu_value",
    "like_complex": "builtins::math::elementwise::plus::tests::plus_like_complex_prototype_yields_complex"
  },
  "description": "`plus(A, B)` (or the operator form `A + B`) adds corresponding elements of `A` and `B`, honouring MATLAB's implicit expansion rules so that scalars and singleton dimensions broadcast automatically.",
  "behaviors": [
    "Supports real, complex, logical, and character inputs; logical and character data are promoted to double precision before addition.",
    "Implicit expansion works across any dimension, provided the non-singleton extents match. Size mismatches raise the standard MATLAB-compatible error message.",
    "Complex operands follow MATLAB's analytic rule `(a + ib) + (c + id) = (a + c) + i(b + d)`.",
    "Empty dimensions propagate naturally—if either operand has a zero-sized dimension after broadcasting, the result is empty with the broadcasted shape.",
    "Integer inputs promote to double precision before addition, mirroring MATLAB behaviour and keeping numeric tower rules consistent with other RunMat arithmetic builtins.",
    "The optional `'like'` prototype makes the output adopt the residency (host or GPU) and complexity characteristics of the prototype, which is particularly useful for keeping implicit-expansion expressions on the device."
  ],
  "examples": [
    {
      "description": "Adding two matrices element-wise",
      "input": "A = [1 2 3; 4 5 6];\nB = [7 8 9; 1 2 3];\nS = plus(A, B)",
      "output": "S =\n     8    10    12\n     5     7     9"
    },
    {
      "description": "Adding a scalar to every element of a matrix",
      "input": "A = magic(3);\nshifted = plus(A, 0.5)",
      "output": "shifted =\n    8.5    1.5    6.5\n    3.5    5.5    9.5\n   10.5    7.5    2.5"
    },
    {
      "description": "Using implicit expansion between a column and row vector",
      "input": "col = (1:3)';\nrow = [10 20 30];\nm = plus(col, row)",
      "output": "m =\n    11    21    31\n    12    22    32\n    13    23    33"
    },
    {
      "description": "Adding complex inputs element-wise",
      "input": "z1 = [1+2i, 3-4i];\nz2 = [2-1i, -1+1i];\nsumz = plus(z1, z2)",
      "output": "sumz =\n     3 + 1i     2 - 3i"
    },
    {
      "description": "Adding character codes to produce numeric arrays",
      "input": "letters = 'ABC';\ncodes = plus(letters, 2)",
      "output": "codes = [67 68 69]"
    },
    {
      "description": "Keeping element-wise sums on the GPU with `'like'`",
      "input": "proto = gpuArray.zeros(1, 1);\nG1 = gpuArray([1 2 3]);\nG2 = gpuArray([4 5 6]);\ndeviceSum = plus(G1, G2, 'like', proto);\nresult = gather(deviceSum)",
      "output": "deviceSum =\n  1x3 gpuArray\n     5     7     9\nresult =\n     5     7     9"
    }
  ],
  "faqs": [
    {
      "question": "Does `plus` support MATLAB implicit expansion?",
      "answer": "Yes. Any singleton dimensions expand automatically. If a dimension has incompatible non-singleton extents, `plus` raises the standard size-mismatch error."
    },
    {
      "question": "What numeric type does `plus` return?",
      "answer": "Results are double precision for real inputs and complex double when either operand is complex. Logical and character inputs are promoted to double before addition."
    },
    {
      "question": "Can I add gpuArrays and host scalars?",
      "answer": "Yes. RunMat keeps the computation on the GPU when the scalar is numeric. For other host operand types, the runtime gathers the gpuArray and computes on the CPU."
    },
    {
      "question": "Does `plus` preserve gpuArray residency after a fallback?",
      "answer": "When a fallback occurs (for example, implicit expansion that the provider does not implement), the resulting array stays on the host by default. Provide a `'like', gpuArray(...)` prototype if you need the runtime to re-upload the final result automatically."
    },
    {
      "question": "How can I force the result to stay on the GPU?",
      "answer": "Provide a `'like'` prototype: `plus(A, B, 'like', gpuArray.zeros(1, 1))` keeps the result on the device even if one of the inputs originated on the host."
    },
    {
      "question": "How are empty arrays handled?",
      "answer": "Empty dimensions propagate. If either operand has an extent of zero in the broadcasted shape, the result is empty with that broadcasted shape."
    },
    {
      "question": "Are integer inputs supported?",
      "answer": "Yes. Integers promote to double precision during the addition, matching other RunMat arithmetic builtins."
    },
    {
      "question": "Can I mix complex and real operands?",
      "answer": "Absolutely. The result is complex, with broadcasting rules identical to MATLAB."
    },
    {
      "question": "What about string arrays?",
      "answer": "String arrays are not numeric and therefore raise an error when passed to `plus`."
    }
  ],
  "links": [
    {
      "label": "mtimes",
      "url": "./mtimes"
    },
    {
      "label": "times",
      "url": "./times"
    },
    {
      "label": "ldivide",
      "url": "./ldivide"
    },
    {
      "label": "rdivide",
      "url": "./rdivide"
    },
    {
      "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": "exp",
      "url": "./exp"
    },
    {
      "label": "expm1",
      "url": "./expm1"
    },
    {
      "label": "factorial",
      "url": "./factorial"
    },
    {
      "label": "gamma",
      "url": "./gamma"
    },
    {
      "label": "hypot",
      "url": "./hypot"
    },
    {
      "label": "imag",
      "url": "./imag"
    },
    {
      "label": "log",
      "url": "./log"
    },
    {
      "label": "log10",
      "url": "./log10"
    },
    {
      "label": "log1p",
      "url": "./log1p"
    },
    {
      "label": "log2",
      "url": "./log2"
    },
    {
      "label": "minus",
      "url": "./minus"
    },
    {
      "label": "pow2",
      "url": "./pow2"
    },
    {
      "label": "power",
      "url": "./power"
    },
    {
      "label": "real",
      "url": "./real"
    },
    {
      "label": "sign",
      "url": "./sign"
    },
    {
      "label": "single",
      "url": "./single"
    },
    {
      "label": "sqrt",
      "url": "./sqrt"
    }
  ],
  "source": {
    "label": "`crates/runmat-runtime/src/builtins/math/elementwise/plus.rs`",
    "url": "https://github.com/runmat-org/runmat/blob/main/crates/runmat-runtime/src/builtins/math/elementwise/plus.rs"
  },
  "gpu_residency": "RunMat's auto-offload planner keeps tensors on the GPU whenever fused expressions benefit from device execution. Explicit `gpuArray` / `gather` calls remain supported for MATLAB code that manages residency manually. When the active provider lacks the kernels needed for a particular call (for example, implicit expansion between gpuArrays of different shapes), RunMat gathers back to the host, computes the MATLAB-accurate result, and resumes execution seamlessly.",
  "gpu_behavior": [
    "When a gpuArray provider is active:\n\n1. If both operands are gpuArrays with identical shapes, RunMat dispatches to the provider's `elem_add` hook. 2. If one operand is a scalar (host or device) and the other is a gpuArray, the runtime calls `scalar_add` to keep the result on the device. 3. The fusion planner treats `plus` as a fusible elementwise node, so adjacent elementwise producers/consumers can execute inside a single WGSL kernel or provider-optimised pipeline, avoiding spurious host↔device transfers. 4. Implicit-expansion workloads (e.g., mixing row and column vectors) or unsupported operand kinds gather transparently to host memory, compute the result with full MATLAB semantics, and return a host tensor unless a `'like'` GPU prototype is supplied—in which case the runtime re-uploads the output to honour the residency request."
  ]
}