u-space / tests /test_simplex.py
loose-bits's picture
Deploy 7123e8e
d58cfe6 verified
Raw History Blame Contribute Delete
3.72 kB
"""The cone's composition as a point in a 3-simplex: coordinates, camera and the mark.
Numpy only; nothing here needs gradio or a model.
"""
import re
import numpy as np
import pytest
from ulens import simplex, terms
def test_the_vertices_are_a_regular_tetrahedron_about_the_origin():
V = simplex.VERT
assert V.shape == (4, 3)
np.testing.assert_allclose(V.sum(0), 0, atol=1e-6) # barycentre at 0
np.testing.assert_allclose(np.linalg.norm(V, axis=1), 1, atol=1e-6) # circumradius 1
d = [np.linalg.norm(V[i] - V[j]) for i in range(4) for j in range(i + 1, 4)]
np.testing.assert_allclose(d, d[0], atol=1e-6) # six equal edges
assert len(terms.categories()) == 4 # one vertex per axis
def test_shares_sum_to_one_and_a_zero_row_is_undefined():
C = np.array([[1.0, 2.0, 3.0, 4.0], [0.5, 0.5, 0.5, 0.5], [0.0, 0.0, 0.0, 0.0]])
W = simplex.shares(C)
np.testing.assert_allclose(W[:2].sum(1), 1.0)
np.testing.assert_allclose(W[0], [0.1, 0.2, 0.3, 0.4])
assert np.isnan(W[2]).all() # no composition at all
assert (simplex.shares(np.full((5, 4), 2.0)) == 0.25).all()
def test_a_pure_one_axis_state_lands_on_its_vertex_and_the_centroid_at_the_origin():
P = simplex.bary(np.eye(4))
np.testing.assert_allclose(P, simplex.VERT, atol=1e-6)
np.testing.assert_allclose(simplex.bary(np.full((1, 4), 0.25)), 0, atol=1e-6)
# a state between two axes sits on their edge's midpoint
np.testing.assert_allclose(simplex.bary(np.array([[0.5, 0.5, 0, 0]]))[0],
(simplex.VERT[0] + simplex.VERT[1]) / 2, atol=1e-6)
def test_boundary_fraction_is_zero_at_the_centre_and_one_on_the_surface():
V = simplex.VERT
assert simplex.boundary_fraction(np.zeros((1, 3)))[0] == pytest.approx(0.0)
assert simplex.boundary_fraction(V)[0] == pytest.approx(1.0) # a vertex
face = V[:3].mean(0) # a face centre
assert simplex.boundary_fraction(face[None])[0] == pytest.approx(1.0)
assert simplex.boundary_fraction((0.5 * face)[None])[0] == pytest.approx(0.5)
assert simplex.boundary_fraction((1.5 * face)[None])[0] > 1.0 # outside
def test_the_camera_sends_the_origin_to_the_screen_centre_and_keeps_the_paper_s_layout():
xy = simplex.project(np.zeros((1, 3)))
np.testing.assert_allclose(xy, 0, atol=1e-9)
amb, inc, ce, gu = simplex.project(simplex.VERT)
assert gu[1] > amb[1] > 0 > inc[1] > ce[1] # general uncertainty on top
assert inc[0] < amb[0] < 0 < gu[0] < ce[0] # incompleteness left, conflict right
# perspective, not orthographic: the near vertex (ambiguity) is pulled toward the eye
orth = simplex.project(simplex.VERT, eye=tuple(1e6 * np.asarray(simplex.EYE)))
assert abs(amb[1]) > abs(orth[0][1])
def test_the_mark_is_the_same_tetrahedron_under_the_same_camera():
"""`data/mark.svg` is generated from `mark_points`; this keeps it from drifting."""
svg = simplex.mark_svg()
assert svg.startswith("<svg") and svg.endswith("</svg>")
assert 'viewBox="0 0 32 32"' in svg
circles = re.findall(r'<circle cx="([\d.]+)" cy="([\d.]+)" r="([\d.]+)" fill="(#[0-9a-fA-F]{6})"',
svg)
assert len(circles) == 4 and svg.count("<line ") == 6 and "<text" not in svg
pts = np.array([[float(x), float(y)] for x, y, _r, _c in circles])
np.testing.assert_allclose(pts, simplex.mark_points(), atol=0.01)
assert (pts >= 0).all() and (pts <= 32).all()
assert simplex.MARK_PATH.name == "mark.svg" and simplex.MARK_PATH.is_file()