runmat-runtime 0.4.1

Core runtime for RunMat with builtins, BLAS/LAPACK integration, and execution APIs
Documentation
{
  "title": "mod",
  "category": "math/rounding",
  "keywords": [
    "mod",
    "modulus",
    "remainder",
    "rounding",
    "gpu"
  ],
  "summary": "Compute the MATLAB-style modulus a - b .* floor(./b) for scalars, matrices, N-D tensors, and complex values.",
  "references": [
    "https://www.mathworks.com/help/matlab/ref/mod.html"
  ],
  "gpu_support": {
    "elementwise": true,
    "reduction": false,
    "precisions": [
      "f32",
      "f64"
    ],
    "broadcasting": "matlab",
    "notes": "Composed from elem_div → unary_floor → elem_mul → elem_sub when all GPU operands share a shape; otherwise RunMat gathers to the host."
  },
  "fusion": {
    "elementwise": true,
    "reduction": false,
    "max_inputs": 2,
    "constants": "inline"
  },
  "requires_feature": null,
  "tested": {
    "unit": "builtins::math::rounding::mod::tests",
    "integration": "builtins::math::rounding::mod::tests::mod_gpu_pair_roundtrip"
  },
  "description": "`C = mod(A, B)` returns the modulus after division such that `C` has the same sign as `B` and satisfies `A = B.*Q + C` with `Q = floor(./B)`. The definition holds for scalars, vectors, matrices, higher-dimensional tensors, and complex numbers.",
  "behaviors": [
    "Works with MATLAB-style implicit expansion (broadcasting) between `A` and `B`.",
    "Returns `NaN` for elements where `B` is zero or both arguments are non-finite in incompatible ways (`Inf` modulo finite, `NaN` inputs, etc.).",
    "Logical and integer inputs are promoted to double precision; character arrays operate on their Unicode code points.",
    "Complex inputs use the MATLAB definition `mod(a, b) = a - b.*floor(a./b)` with complex division and component-wise `floor`.",
    "Empty arrays propagate emptiness while retaining their shapes."
  ],
  "examples": [
    {
      "description": "Computing the modulus of positive integers",
      "input": "r = mod(17, 5)",
      "output": "r = 2"
    },
    {
      "description": "Modulus with negative divisors keeps the divisor's sign",
      "input": "values = [-7 -3 4 9];\nmods   = mod(values, -4)",
      "output": "mods = [-3 -3 0 -3]"
    },
    {
      "description": "Broadcasting a scalar divisor across a matrix",
      "input": "A = [4.5  7.1; -2.3  0.4];\nresult = mod(A, 2)",
      "output": "result =\n    [0.5  1.1;\n     1.7  0.4]"
    },
    {
      "description": "MATLAB-compatible modulus for complex numbers",
      "input": "z = [3 + 4i, -2 + 5i];\ndiv = 2 + 1i;\nres = mod(z, div)",
      "output": "res =\n    [0.0 + 0.0i, 0.0 + 1.0i]"
    },
    {
      "description": "Handling zeros in the divisor",
      "input": "warn = mod([2, 0, -2], [0, 0, 0])",
      "output": "warn = [NaN NaN NaN]"
    },
    {
      "description": "Using `mod` with character arrays",
      "input": "letters = mod('ABC', 5)",
      "output": "letters = [0 1 2]"
    },
    {
      "description": "Staying on the GPU when hooks are available",
      "input": "G = gpuArray(-5:5);\nH = mod(G, 4);\ncpuCopy = gather(H)",
      "output": "cpuCopy = [3 0 1 2 3 0 1 2 3 0 1]"
    }
  ],
  "faqs": [
    {
      "question": "How is `mod` different from `rem`?",
      "answer": "`mod` uses `floor` and keeps the sign of the divisor. `rem` uses `fix` and keeps the sign of the dividend."
    },
    {
      "question": "What happens when the divisor is zero?",
      "answer": "The result is `NaN` (or `NaN + NaNi` for complex inputs), matching MATLAB semantics."
    },
    {
      "question": "Does `mod` support complex numbers?",
      "answer": "Yes. Both operands can be complex; the runtime applies MATLAB's definition with complex division and component-wise `floor`."
    },
    {
      "question": "Do GPU sources need identical shapes?",
      "answer": "Yes. The device fast path currently requires both operands to share the same shape. Other cases fall back to the CPU implementation automatically."
    },
    {
      "question": "Are empty arrays preserved?",
      "answer": "Yes. Empty inputs return empty outputs with the same shape."
    },
    {
      "question": "Will `mod` ever change integer classes?",
      "answer": "Inputs promote to double precision internally; results are reported as double scalars or tensors, mirroring MATLAB's default numeric type."
    }
  ],
  "links": [
    {
      "label": "rem",
      "url": "./rem"
    },
    {
      "label": "floor",
      "url": "./floor"
    },
    {
      "label": "fix",
      "url": "./fix"
    },
    {
      "label": "gpuArray",
      "url": "./gpuarray"
    },
    {
      "label": "gather",
      "url": "./gather"
    },
    {
      "label": "ceil",
      "url": "./ceil"
    },
    {
      "label": "round",
      "url": "./round"
    }
  ],
  "source": {
    "label": "`crates/runmat-runtime/src/builtins/math/rounding/mod.rs`",
    "url": "https://github.com/runmat-org/runmat/blob/main/crates/runmat-runtime/src/builtins/math/rounding/mod.rs"
  },
  "gpu_residency": "Usually not. When the provider exposes the elementwise hooks noted above, `mod` executes entirely on the GPU. Otherwise RunMat gathers transparently, ensuring MATLAB-compatible behaviour without manual intervention. Explicit `gpuArray` / `gather` calls remain available for scripts that mirror MathWorks MATLAB workflows.",
  "gpu_behavior": [
    "When both operands are GPU tensors with the same shape, RunMat composes `mod` from the provider hooks `elem_div`, `unary_floor`, `elem_mul`, and `elem_sub`. This keeps the computation on the device when those hooks are implemented (the shipped WGPU backend and in-process provider expose them). For mixed residency, shape-mismatched operands, or providers that lack any of these hooks, RunMat gathers to the host, applies the CPU implementation, and returns a host-resident result."
  ]
}