BRAGA

02 Writing02-02 Notes02-02-274

Inverse Cosine

Published 16 Apr 20268 min read
Contents
  1. 01Deep Breakdown
  2. 02Flowchart
  3. 03Real-world use
  4. 04The formula, piece by piece
  5. 05The formula in words
  6. 06Critical Concepts
  7. 07Step-by-step calculation (tiny example)
  8. 08Code examples
ddx(arccos⁡(ax))=−a1−(ax)2\frac{d}{dx}(\arccos(ax)) = \frac{-a}{\sqrt{1 - (ax)^2}}
(1)

Deep Breakdown

What it tells me (Situation Awareness): The derivative of arccos⁡(ax)\arccos(ax) is −a/1−(ax)2-a/\sqrt{1-(ax)^2} — identical in magnitude to the arcsin⁡\arcsin derivative, but negative. This negative sign reflects that arccos⁡\arccos is a strictly decreasing function: as xx increases, arccos⁡(ax)\arccos(ax) decreases. The key identity arcsin⁡(x)+arccos⁡(x)=π/2\arcsin(x) + \arccos(x) = \pi/2 (for x∈[−1,1]x \in [-1, 1]) immediately implies the derivatives are negatives of each other, since d/dx(π/2)=0d/dx(\pi/2) = 0. In DA/DS: arccos⁡\arccos appears in computing angular distances between unit vectors (cosine similarity to angle conversion: θ=arccos⁡(u⋅v)\theta = \arccos(\mathbf{u} \cdot \mathbf{v})), in computing gradients through similarity metrics, and in geodesic distance formulas used in geospatial and graph ML.

Flowchart

f(x) = arccos(ax)Method 1:implicitdifferentiationcos(y) = ax,differentiate:-sin(y)*y' = ay' = -a / sin(y) =-a / sqrt(1 -cos^2(y)) = -a /sqrt(1 - (ax)^2)Method 2: useidentity arcsin +arccos = pi/2d/dx(arccos(ax))=-d/dx(arcsin(ax))= -a / sqrt(1 -(ax)^2)Domain: |ax| <1, derivative isnegative (arccosis decreasing)Chain rule:d/dx(arccos(f(x))) = -f'(x) / sqrt(1 -f(x)^2)

Real-world use

Where it is used

Computing the gradient of angular distance θ=arccos⁡(cosine_similarity)\theta = \arccos(\text{cosine\_similarity}) when using angular loss functions in metric learning and contrastive learning; differentiating geodesic distance formulas for spherical coordinates in geospatial ML; computing gradients through arccos⁡\arccos-based activation functions; deriving the gradient of the angular margin in ArcFace loss for face recognition.

Problem it solves

When optimizing over angular quantities (e.g., minimizing the angle between embedding vectors), the gradient must flow through arccos⁡\arccos. Without this derivative, backpropagation through angular loss functions cannot be computed.

How it solves the problem

Implicit differentiation: let y=arccos⁡(ax)y = \arccos(ax), so cos⁡(y)=ax\cos(y) = ax. Differentiate: −sin⁡(y)⋅y′=a-\sin(y) \cdot y' = a, giving y′=−a/sin⁡(y)y' = -a/\sin(y). Since y∈[0,π]y \in [0, \pi], sin⁡(y)≥0\sin(y) \ge 0, and sin⁡(y)=1−cos⁡2(y)=1−(ax)2\sin(y) = \sqrt{1 - \cos^2(y)} = \sqrt{1-(ax)^2}. Therefore y′=−a/1−(ax)2y' = -a/\sqrt{1-(ax)^2}.

Example in a business

A DS trains a recommendation model with ArcFace-style angular margin loss. The logit for class kk is cos⁡(θwk,x)=wk⋅x/(∥wk∥∥x∥)\cos(\theta_{w_k, x}) = w_k \cdot x / (\|w_k\|\|x\|), and the angular distance θ=arccos⁡(cos⁡)\theta = \arccos(\cos) is needed. The gradient dθ/d(cos⁡θ)=−1/1−cos⁡2θ=−1/sin⁡θd\theta/d(\cos\theta) = -1/\sqrt{1-\cos^2\theta} = -1/\sin\theta uses exactly d/du(arccos⁡u)=−1/1−u2d/du(\arccos u) = -1/\sqrt{1-u^2} (with a=1a=1). This gradient flows back through the embedding layer.

The formula, piece by piece

  • arccos⁡\arccos: inverse cosine function; maps [−1,1]→[0,π][-1, 1] \to [0, \pi]; decreasing function
  • axax: the argument; domain requires ∣ax∣≤1|ax| \le 1, i.e., ∣x∣≤1/a|x| \le 1/a
  • −a-a: numerator; the negative sign because arccos⁡\arccos is decreasing
  • 1−(ax)2\sqrt{1-(ax)^2}: denominator; identical to the arcsin⁡\arcsin case
  • Base case: d/dx(arccos⁡x)=−1/1−x2d/dx(\arccos x) = -1/\sqrt{1-x^2}
  • Identity: arcsin⁡x+arccos⁡x=π/2⇒d/dx(arccos⁡x)=−d/dx(arcsin⁡x)\arcsin x + \arccos x = \pi/2 \Rightarrow d/dx(\arccos x) = -d/dx(\arcsin x)
  • General chain rule: d/dx(arccos⁡f(x))=−f′(x)/1−f(x)2d/dx(\arccos f(x)) = -f'(x)/\sqrt{1-f(x)^2}
  • Cosine similarity gradient: d/d(sim)(arccos⁡(sim))=−1/1−sim2d/d(\text{sim})(\arccos(\text{sim})) = -1/\sqrt{1-\text{sim}^2}

The formula in words

"The derivative of arccos⁡(ax)\arccos(ax) is the negative of the arcsin⁡(ax)\arcsin(ax) derivative. It is aa in the numerator (chain rule factor), 1−argument2\sqrt{1 - \text{argument}^2} in the denominator, and a minus sign because arccos⁡\arccos decreases as xx increases. The magnitude is identical to arcsin⁡\arcsin, only the direction flips." Tiny examples: d/dx(arccos⁡x)∣x=0=−1/1−0=−1d/dx(\arccos x)|_{x=0} = -1/\sqrt{1-0} = -1. d/dx(arccos⁡x)∣x=0.5=−1/1−0.25=−1/0.75≈−1.155d/dx(\arccos x)|_{x=0.5} = -1/\sqrt{1-0.25} = -1/\sqrt{0.75} \approx -1.155. d/dx(arccos⁡(2x))∣x=0=−2/1−0=−2d/dx(\arccos(2x))|_{x=0} = -2/\sqrt{1-0} = -2. d/du(arccos⁡u)∣u=0.8=−1/1−0.64=−1/0.36=−1/0.6≈−1.667d/du(\arccos u)|_{u=0.8} = -1/\sqrt{1-0.64} = -1/\sqrt{0.36} = -1/0.6 \approx -1.667.

Critical Concepts

  • Negative derivative = decreasing function: arccos⁡\arccos goes from π\pi at x=−1x=-1 to 00 at x=1x=1. Its derivative is always negative (except undefined at the endpoints). This is the opposite of arcsin⁡\arcsin, which increases from −π/2-\pi/2 to π/2\pi/2.
  • Complementary identity: arcsin⁡(x)+arccos⁡(x)=π/2\arcsin(x) + \arccos(x) = \pi/2 for all x∈[−1,1]x \in [-1, 1]. Differentiating both sides: d/dx(arcsin⁡x)+d/dx(arccos⁡x)=0d/dx(\arcsin x) + d/dx(\arccos x) = 0, so d/dx(arccos⁡x)=−d/dx(arcsin⁡x)=−1/1−x2d/dx(\arccos x) = -d/dx(\arcsin x) = -1/\sqrt{1-x^2}. This is the quickest proof of the formula.
  • Angular gradient in embeddings: when θ=arccos⁡(u⋅v)\theta = \arccos(\mathbf{u} \cdot \mathbf{v}) and u,v\mathbf{u}, \mathbf{v} are unit vectors, the gradient of θ\theta with respect to the cosine similarity is −1/sin⁡θ-1/\sin\theta. This blows up when θ≈0\theta \approx 0 or θ≈π\theta \approx \pi (nearly identical or opposite vectors). Clip or add ϵ\epsilon in practice.
  • Same domain restriction as arcsin: ∣ax∣<1|ax| < 1 required. Inputs outside [−1,1][-1, 1] to arccos⁡\arccos are undefined. When computing arccos⁡(cosine_similarity)\arccos(\text{cosine\_similarity}), numerical errors can push the similarity slightly outside [−1,1][-1, 1]; always clip first: sim = np.clip(sim, -1, 1).
  • Gradient clipping at boundary: just like arcsin⁡\arcsin, the derivative →−∞\to -\infty as ∣ax∣→1|ax| \to 1.

In ArcFace and similar angular losses, this is handled by clipping the cosine similarity away from ±1\pm 1.

Step-by-step calculation (tiny example)

Compute the gradient of angular distance θ=arccos⁡(sim)\theta = \arccos(\text{sim}) at sim=0.6\text{sim} = 0.6.

Step 1: Identify. θ=arccos⁡(u)\theta = \arccos(u) with u=sim=0.6u = \text{sim} = 0.6, a=1a = 1.

Step 2: Apply formula. dθ/du=−1/1−u2d\theta/du = -1/\sqrt{1-u^2}.

Step 3: Evaluate. 1−0.62=1−0.36=0.641 - 0.6^2 = 1 - 0.36 = 0.64. 0.64=0.8\sqrt{0.64} = 0.8. dθ/du=−1/0.8=−1.25d\theta/du = -1/0.8 = -1.25. Interpretation: at cosine similarity =0.6= 0.6, a unit increase in similarity decreases the angle by 1.251.25 radians (about 71.6°71.6°). Verify: arccos⁡(0.6+10−7)−arccos⁡(0.6−10−7)\arccos(0.6 + 10^{-7}) - \arccos(0.6 - 10^{-7}) divided by 2×10−7≈−1.252 \times 10^{-7} \approx -1.25. ✓\checkmark

Code examples

Beginner example (Python)

python
import numpy as np

def arccos_ax_deriv(a: float, x: float) -> float:
    """d/dx(arccos(ax)) = -a / sqrt(1 - (ax)^2). Requires |ax| < 1."""
    arg = a * x
    if abs(arg) >= 1.0:
        raise ValueError(f"|ax| = {abs(arg):.4f} >= 1: outside arccos domain.")
    return -a / np.sqrt(1.0 - arg**2)   # Verify numerically
h = 1e-7
test_cases = [(1.0, 0.5), (2.0, 0.3), (0.5, 0.8), (3.0, 0.1)]
print(f"{'a':>6} {'x':>6} {'Analytic':>12} {'Numerical':>12} {'Match':>7}")
for a, x in test_cases:
    analytic = arccos_ax_deriv(a, x)
numerical = (np.arccos(a*(x+h)) - np.arccos(a*(x-h))) / (2 * h)
match = abs(analytic - numerical) < 1e-5
print(f"{a:>6.2f} {x:>6.3f} {analytic:>12.6f} {numerical:>12.6f} {str(match):>7}")   # Angular distance gradient for cosine similarity
print("\nAngular distance gradient d(arccos(sim))/d(sim):")
for sim in [0.0, 0.3, 0.6, 0.8, 0.9, 0.99]:
    grad = arccos_ax_deriv(1.0, sim)
angle_deg = np.degrees(np.arccos(sim))
print(f" sim={sim:.2f} (theta={angle_deg:.1f} deg): gradient = {grad:.4f}")

Beginner example (R)

r
# d/dx(arccos(ax)) = -a / sqrt(1 - (ax)^2)
arccos_ax_deriv <- function(a, x) { arg <- a * x
  if (abs(arg) >= 1) stop(sprintf("|ax| = %.4f >= 1: outside domain", abs(arg))) -a / sqrt(1 - arg^2)
}  # Verify with complement identity: d/dx(arccos) = -d/dx(arcsin)
arcsin_ax_deriv <- function(a, x) a / sqrt(1 - (a*x)^2)
cat("Complement check: arccos_deriv + arcsin_deriv should = 0\n")
for (x in c(0.0, 0.3, 0.6, 0.8)) { sum_derivs <- arccos_ax_deriv(1, x) + arcsin_ax_deriv(1, x)
  cat(sprintf(" x=%.1f: arccos'+ arcsin' = %.8f\n", x, sum_derivs))
}

Professional example (Python)

python
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
np.random.seed(42)   # -- Angular loss gradient: d/d(embedding) of arccos(cosine_similarity) --
# Use case: metric learning where angular distance between embeddings is minimized.
# Gradient flows back through arccos to update embedding parameters.
EMBED_DIM = 64
N_SAMPLES = 100

def unit_normalize(x: np.ndarray) -> np.ndarray:
    """L2-normalize each row to lie on unit sphere."""
    norms = np.linalg.norm(x, axis=1, keepdims=True)
    return x / (norms + 1e-10)

def arccos_gradient_wrt_sim(cos_sim: np.ndarray, epsilon: float = 1e-6) -> np.ndarray:
    """d/d(cos_sim)(arccos(cos_sim)) = -1 / sqrt(1 - cos_sim^2). Clip cos_sim away from +-1 to avoid infinite gradient. """
    cos_sim_clipped = np.clip(cos_sim, -1 + epsilon, 1 - epsilon)
    return -1.0 / np.sqrt(1.0 - cos_sim_clipped**2)

def angular_loss_and_gradient( embeds_a: np.ndarray, embeds_b: np.ndarray ) -> tuple:
    """Angular distance loss = mean arccos(cos_sim(a, b)). Gradient w.r.t. cos_sim: -1/sqrt(1 - sim^2). """
    sim = np.sum(embeds_a * embeds_b, axis=1)   # dot product of unit vectors = cosine sim
    sim_clipped = np.clip(sim, -1 + 1e-6, 1 - 1e-6)
    angles = np.arccos(sim_clipped)
    loss = np.mean(angles)
    grad_wrt_sim = arccos_gradient_wrt_sim(sim)
    return float(loss), grad_wrt_sim, angles   # Simulate two batches of embeddings
embeds_a = unit_normalize(np.random.randn(N_SAMPLES, EMBED_DIM))
embeds_b = unit_normalize(np.random.randn(N_SAMPLES, EMBED_DIM))
loss, grad_sim, angles = angular_loss_and_gradient(embeds_a, embeds_b)
cos_sims = np.sum(embeds_a * embeds_b, axis=1)
print("Angular Distance Loss: gradient via d/d(sim)(arccos(sim)) = -1/sqrt(1 - sim^2)")
print(f"\nBatch size: {N_SAMPLES}, embed dim: {EMBED_DIM}")
print(f"Cosine similarity stats: mean={cos_sims.mean():.4f}, std={cos_sims.std():.4f}")
print(f" Range: [{cos_sims.min():.4f}, {cos_sims.max():.4f}]")
print(f"Angular distance (degrees): mean={np.degrees(angles).mean():.2f}, " f"range=[{np.degrees(angles.min()):.2f}, {np.degrees(angles.max()):.2f}]")
print(f"\nAngular loss: {loss:.6f} rad = {np.degrees(loss):.2f} deg")
print(f"\nGradient d(loss)/d(sim) stats:")
print(f" mean={grad_sim.mean():.4f}, range=[{grad_sim.min():.4f}, {grad_sim.max():.4f}]")
print(f"\nKey: most negative gradient at sim close to +/-1 (large angle change per similarity change)")
max_grad_idx = np.argmax(np.abs(grad_sim))
print(f" Most extreme gradient: sim={cos_sims[max_grad_idx]:.4f}, " f"grad={grad_sim[max_grad_idx]:.4f} (theta={np.degrees(angles[max_grad_idx]):.2f} deg)")

Source: domain knowledge

#calculus#differential-calculus#table-of-standard-derivatives

Keyboard shortcuts

Single keys, ignored while typing in a field or with Cmd, Ctrl or Alt held.

0
Home
1 2 3 4
Work, Writing, Tools, About
j / k
Next / previous section on this page
n / p
Next / previous project or essay
u
Up one level
l
Deutsch
t
Light or dark theme
?
This list
Esc
Close