{
"title": "acosh",
"category": "math/trigonometry",
"keywords": [
"acosh",
"inverse hyperbolic cosine",
"arccosh",
"trigonometry",
"gpu",
"complex"
],
"summary": "Element-wise inverse hyperbolic cosine with MATLAB-compatible complex promotion and GPU fallbacks.",
"references": [],
"gpu_support": {
"elementwise": true,
"reduction": false,
"precisions": [
"f32",
"f64"
],
"broadcasting": "matlab",
"notes": "Uses the provider's unary_acosh hook when every element stays within the real domain (x ≥ 1); gathers to the host when complex promotion is required or hooks are unavailable."
},
"fusion": {
"elementwise": true,
"reduction": false,
"max_inputs": 1,
"constants": "inline"
},
"requires_feature": null,
"tested": {
"unit": "builtins::math::trigonometry::acosh::tests",
"integration": "builtins::math::trigonometry::acosh::tests::acosh_gpu_provider_roundtrip"
},
"description": "`Y = acosh(X)` evaluates the inverse hyperbolic cosine of each element in `X`. Real inputs greater than or equal to `1` stay real; values below `1` automatically promote to complex results that match MATLAB's principal branch.",
"behaviors": [
"Accepts scalars, vectors, matrices, and N-D tensors with MATLAB broadcasting semantics.",
"Logical inputs are promoted to double precision (`true → 1`, `false → 0`) before evaluation.",
"Character arrays are interpreted as their numeric code points. If any element is less than `1`, the result becomes a complex array, mirroring MATLAB.",
"Real values in `[1, ∞)` return real outputs computed by `acosh`.",
"Real values in `(-∞, 1)` produce complex results: the runtime returns `Value::Complex` or `Value::ComplexTensor` so that downstream code sees the same behaviour as MATLAB.",
"Complex inputs follow MATLAB's definition `acosh(z) = \\log(z + \\sqrt{z-1}\\sqrt{z+1})`, including NaN/Inf handling on branch cuts.",
"Special values propagate exactly like MATLAB: `acosh(NaN) = NaN`, `acosh(Inf) = Inf`, and `acosh(-Inf) = Inf + i·π`."
],
"examples": [
{
"description": "Inverse hyperbolic cosine of a scalar greater than one",
"input": "y = acosh(1.5)",
"output": "y = 0.9624"
},
{
"description": "Applying `acosh` to each element of a vector",
"input": "x = [1 1.5 2 4];\ny = acosh(x)",
"output": "y = [0 0.9624 1.31696 2.06344]"
},
{
"description": "Handling elements below one that produce complex results",
"input": "values = [0.5 1 2];\nz = acosh(values)",
"output": "z =\n 0.0000 + 1.0472i 0.0000 + 0.0000i 1.31696 + 0.0000i"
},
{
"description": "Computing `acosh` on GPU-resident data when the domain stays real",
"input": "G = gpuArray(linspace(1, 5, 5));\nresult_gpu = acosh(G);\nresult = gather(result_gpu)",
"output": "result = [0 1.31696 1.76274 2.06344 2.29243]"
},
{
"description": "Evaluating `acosh` for complex numbers",
"input": "z = [1 + 2i, -2 + 0.5i];\nw = acosh(z)",
"output": "w =\n 1.5286 + 1.1437i\n 1.3618 + 2.8638i"
},
{
"description": "Working with character arrays (mix of complex and real results)",
"input": "C = char([0 65]); % includes a code point below 1\nY = acosh(C)",
"output": "Y =\n 0.0000 + 1.5708i 4.8675 + 0.0000i"
}
],
"faqs": [
{
"question": "Why does `acosh` sometimes return complex numbers?",
"answer": "The real-valued inverse hyperbolic cosine is only defined for `x ≥ 1`. Inputs below that range require complex results, so RunMat (like MATLAB) promotes them automatically."
},
{
"question": "Can `acosh` run entirely on the GPU?",
"answer": "Yes—when all elements are ≥ 1 and the provider implements `unary_acosh`, the runtime executes the operation on the GPU. Otherwise, it falls back to the host transparently."
},
{
"question": "How are NaN or Inf values handled?",
"answer": "`acosh(NaN)` returns `NaN`. Positive infinity stays real infinity. Negative infinity produces the same complex result as MATLAB (`Inf + i·π`)."
},
{
"question": "Do logical and integer inputs work?",
"answer": "Yes. They are promoted to double precision before evaluation. The output is a dense double (or complex double) array following MATLAB semantics."
},
{
"question": "Can I keep complex results on the GPU?",
"answer": "Currently, GPU tensors represent real data. When complex outputs are required, the runtime gathers to the host and returns `Value::Complex` / `Value::ComplexTensor` for correctness."
},
{
"question": "Does `acosh` participate in fusion?",
"answer": "Yes. The fusion planner treats `acosh` as an element-wise operation and can inline it into fused WGSL kernels when the provider supports the generated code."
},
{
"question": "What tolerance does the runtime use to decide GPU fallback?",
"answer": "Any element below `1` triggers a host fallback so the runtime can return the correct complex result. This mirrors MATLAB exactly instead of relying on GPU intrinsics that would otherwise yield `NaN`."
},
{
"question": "Can `acosh` be differentiated automatically?",
"answer": "Yes. Marking it as an element-wise builtin ensures future autodiff tooling can reuse the same metadata to generate gradients."
}
],
"links": [
{
"label": "asinh",
"url": "./asinh"
},
{
"label": "tanh",
"url": "./tanh"
},
{
"label": "cosh",
"url": "./cosh"
},
{
"label": "gpuArray",
"url": "./gpuarray"
},
{
"label": "gather",
"url": "./gather"
},
{
"label": "acos",
"url": "./acos"
},
{
"label": "asin",
"url": "./asin"
},
{
"label": "atan",
"url": "./atan"
},
{
"label": "atan2",
"url": "./atan2"
},
{
"label": "atanh",
"url": "./atanh"
},
{
"label": "cos",
"url": "./cos"
},
{
"label": "sin",
"url": "./sin"
},
{
"label": "sinh",
"url": "./sinh"
},
{
"label": "tan",
"url": "./tan"
}
],
"source": {
"label": "`crates/runmat-runtime/src/builtins/math/trigonometry/acosh.rs`",
"url": "https://github.com/runmat-org/runmat/blob/main/crates/runmat-runtime/src/builtins/math/trigonometry/acosh.rs"
},
"gpu_residency": "You usually do **not** need to call `gpuArray` manually. The auto-offload planner keeps tensors on the GPU whenever the provider exposes `unary_acosh` and the input stays within the real domain. When complex promotion is needed, RunMat gathers automatically and still returns MATLAB-compatible results. Manual `gpuArray` / `gather` remains available for workflows that require explicit residency control.",
"gpu_behavior": [
"RunMat Accelerate keeps tensors on the GPU when:\n\n1. A provider is registered and implements the `unary_acosh` hook. 2. Every element is provably within the real domain (that is, all elements ≥ 1). 3. All inputs are finite; NaN or `-Inf` values force a host fallback so complex promotion and branch-cut semantics remain correct.\n\nThe runtime calls `reduce_min` on the active provider to perform this domain check. If the provider does not expose the hook, the reduction fails, or any element violates the conditions above, RunMat falls back to the host, evaluates `acosh` with the CPU reference implementation, and returns results identical to MATLAB. This ensures correctness without forcing users to manually `gather` tensors."
]
}