"""The cone's composition as a point in a 3-simplex, and the tetrahedron that is the mark. The four rectified components, normalised to sum to one, are barycentric coordinates in a 3-simplex: the positive orthant the cone lives in is exactly a regular tetrahedron whose four vertices are the four axes, so a state's composition is drawn with nothing reduced or projected away. Near a vertex, one axis carries the cone; at the barycentre all four contribute equally. What the picture drops is the magnitude — how strongly the state pointed — and only that. Numpy only. The demo draws the view as inline SVG through `project`, and the same camera gives the page its mark (`data/mark.svg`, the favicon and the eyebrow): four vertex dots in the axis colours on six thin edges, nothing else. """ from __future__ import annotations from importlib.resources import files import numpy as np #: a regular tetrahedron with its barycentre at the origin and circumradius 1. Row order is #: the category order (`ambiguity, incompleteness, conflicting_evidence, general_uncertainty`). VERT = np.array([[1, 1, 1], [1, -1, -1], [-1, 1, -1], [-1, -1, 1]], np.float32) / np.sqrt(3.0) #: the paper's camera (its simplex-path figures): the eye, looking at the barycentre, z up. EYE = (1.08, 1.87, 0.79) #: the mark's file inside the package, so `pip install` ships it and `launch` can serve it MARK_PATH = files("ulens.data") / "mark.svg" def shares(C: np.ndarray) -> np.ndarray: """`[T, 4]` non-negative components as shares summing to one; a row of zeros is NaN.""" C = np.asarray(C, np.float64) tot = C.sum(axis=-1, keepdims=True) with np.errstate(invalid="ignore", divide="ignore"): return np.where(tot > 0, C / tot, np.nan) def bary(W: np.ndarray) -> np.ndarray: """`[T, 4]` shares to `[T, 3]` points inside the tetrahedron. Linear: a pure one-axis state is that axis's vertex and equal shares are the origin.""" return np.asarray(W, np.float64) @ VERT.astype(np.float64) def boundary_fraction(P: np.ndarray) -> np.ndarray: """How far out each `[T, 3]` point sits, as the fraction of the way from the barycentre to the tetrahedron's surface along its own direction: 0 at the centre, 1 on a face. The face opposite vertex `i` has outward normal `-VERT[i]` and sits at distance 1/3.""" P = np.asarray(P, np.float64) d = -(P @ VERT.T.astype(np.float64)) # [T, 4] signed distance along each normal return np.max(d, axis=-1) * 3.0 def project(P: np.ndarray, eye=EYE) -> np.ndarray: """`[T, 3]` to `[T, 2]` screen coordinates (y up) under a perspective camera at `eye` looking at the origin with z up. The origin projects to (0, 0).""" e = np.asarray(eye, np.float64) d0 = np.linalg.norm(e) f = -e / d0 # forward r = np.cross(f, np.array([0.0, 0.0, 1.0])) r /= np.linalg.norm(r) # right u = np.cross(r, f) # up on screen rel = np.asarray(P, np.float64) - e depth = rel @ f return np.column_stack([(rel @ r) * d0 / depth, (rel @ u) * d0 / depth]) def mark_points(size: float = 32.0, margin: float = 4.6) -> np.ndarray: """The four vertices on a `size`-square canvas (y down), the projected frame fitted inside `margin`. This is where the dots of `data/mark.svg` are.""" xy = project(VERT) lo, hi = xy.min(0), xy.max(0) s = (size - 2 * margin) / float((hi - lo).max()) ctr = (lo + hi) / 2 out = (xy - ctr) * s out[:, 1] *= -1 return out + size / 2 def mark_svg() -> str: """The mark's markup, for inline use; the file itself is the favicon.""" return MARK_PATH.read_text(encoding="utf-8").strip()