librarylibs/Manifold/src/Manifold.xtl

Manifold: the unit sphere, the Poincare ball, and the pictures of the circle, the sphere, the torus and the Swiss roll: the distance measured along the space, putting a point onto it, the tangent part of a vector at a point, a step from a point along a tangent vector that stays on the space and the arrow that undoes it, a mean that stays on the space, how far off it a point is; the points of each space at given coordinates of its map, for drawing it; and the distortion of an embedding's distances against those it should keep. Import it with an alias of your choice: "mf:" u_se< "Manifold". Put libs/Manifold/src on XETAL_PATH ("just path"); the reference is libs/Manifold/docs. Names with l: are exported; those under h: are private to this file.

A point is a vector of Floats, in any number of dimensions; a set of points is a matrix, one point per row. Each space has a name that starts its functions (s_phere, b_all the Poincare ball, c_ircle, t_orus, r_oll the Swiss roll), and the word after it is the same verb for every space: Project (onto it), Distance (along it), Tangent (the part of a vector along it at a point), Step (from a point, along a tangent vector, staying on it), Arrow (the tangent vector a Step would take from one point to another), Mean (of a set, on it), Off (how far a point is from it); and for drawing, Points (the points at rows of coordinates of the space's map), Arc or Geodesic (points along the shortest path between two points). So a program written for the sphere reads the same on the ball. Every result is a Float.

source · imports tr: work/xetal-libraries/libs/Trig/src/Trig.xtl

Vectors

ʰd̲ot : (Num a, Num b) => a -> b -> Float

function (private) · line 32

u d̲ot v: the sum of the products, as a Float.

ʰd̲ot ← { u v → '+ r̲/ (f̲loat u) × f̲loat v }

ʰn̲orm : Num a => a -> Float

function (private) · line 35

n̲orm v: the length of v, straight through the space it sits in.

ʰn̲orm ← { v → s̲qrt v ʰd̲ot v }

ʰs̲caleRows : Num a => a -> a -> a

function (private) · line 38

s v s̲caleRows M: each row of M scaled by the matching item of s.

ʰs̲caleRows ← { s M → M × s 'l̲eft t̲able o̲ffsets 2 s̲elect s̲hape M }
Used in: ʰa̲rrows

ʰa̲ngle : (Num a, Num b) => a -> b -> Float

function (private) · line 45

m a̲ngle s: the angle, 0 to pi, between two unit vectors p and q, from the lengths m = |p - q| and s = |p + q|: 2 atan2(m, s). Accurate near 0 and near pi alike, where a_cos of the product p.q is not (near 0 it loses half the digits), because neither length is a difference of nearly equal numbers.

ʰa̲ngle ← { m s → 2.0 × (f̲loat m) ᵗʳa̲tan2 f̲loat s }

ʰa̲part : (Num a, Num b) => a -> b -> (Float, Float)

function (private) · line 48

p a̲part q: |p - q| and |p + q|, as a tuple.

ʰa̲part ← { p q → (ʰn̲orm (f̲loat p) − f̲loat q, ʰn̲orm (f̲loat p) + f̲loat q) }

ʰa̲partRows : (Num a, Num b) => a -> b -> (Float, Float)

function (private) · line 51

P a̲partRows p: |r - p| and |r + p| for every row r of P.

ʰa̲partRows ← { P p →
  Q ← ((f̲irst s̲hape P) r̲eshape 1.0) '× t̲able f̲loat p         ⍝ p in every row
  D ← (f̲loat P) − Q
  S ← (f̲loat P) + Q
  (s̲qrt '+ r̲/₂ D × D, s̲qrt '+ r̲/₂ S × S)
}
Used in: ʰa̲rrows

ʰa̲partPairs : Num a => a -> (Float, Float)

function (private) · line 61

a̲partPairs P: |r - r'| and |r + r'| for every pair of rows of P, two matrices, summed one coordinate at a time (so no number is a difference of nearly equal ones).

ʰa̲partPairs ← { P →
  n ← f̲irst s̲hape P
  zero ← (n c̲at n) r̲eshape 0.0
  (D, S, _) ← (2 s̲elect s̲hape P) '{ (d, s, k) →
    c ← f̲loat k s̲elect₂ P
    (d + (c '− t̲able c) ^ 2.0, s + (c '+ t̲able c) ^ 2.0, k + 1)
  } p̲ower (zero, zero, 1)
  (s̲qrt D, s̲qrt S)
}

The unit sphere

ˡs̲phereProject : Num a => a -> Float

function · line 82

s̲phereProject v: v put onto the unit sphere, scaled to length 1; the direction of v, kept. Given a set (a matrix), every row. The zero vector has no direction and is refused.

      "mf:" u̲se< "Manifold"
      ᵐᶠs̲phereProject 3.0 4.0 0.0
0.6 0.8 0.0
      ᵐᶠs̲phereProject 2 3 r̲eshape 3.0 4.0 0.0 0.0 0.0 2.0
0.6 0.8 0.0
0.0 0.0 1.0
ˡs̲phereProject ← { v →
  2 = t̲ally s̲hape v ? ʰp̲rojectRows v
  n ← ʰn̲orm v
  n = 0.0 ? @ p̲anic< "the zero vector has no direction: it cannot be put onto the sphere"
  (f̲loat v) ÷ n
}
p̲anic< expands to
(⎕P̲ANIC ("the zero vector has no direction: it cannot be put onto the sphere"))

ʰp̲rojectRows : Num a => a -> Float

function (private) · line 90

Every row of P put onto the sphere.

ʰp̲rojectRows ← { P →
  n ← s̲qrt '+ r̲/₂ (f̲loat P) × f̲loat P
  '∨ r̲/ n = 0.0 ? @ p̲anic< "a zero row has no direction: it cannot be put onto the sphere"
  (f̲loat P) ÷ n 'l̲eft t̲able o̲ffsets 2 s̲elect s̲hape P
}
p̲anic< expands to
(⎕P̲ANIC ("a zero row has no direction: it cannot be put onto the sphere"))

ˡs̲phereDistance : (Num a, Num b) => a -> b -> Float

function · line 104

p s̲phereDistance q: the great-circle distance between two points of the sphere, the angle between them in radians (0 to pi): the shortest path along the sphere, as against the straight line through it, which is shorter (its length is 2 sin of half the angle). p and q must be on the sphere (s_phereProject).

      "mf:" u̲se< "Manifold"
      1.0 0.0 0.0 ᵐᶠs̲phereDistance 0.0 1.0 0.0
1.5707963267948966
ˡs̲phereDistance ← { p q → (m, s) ← p ʰa̲part q◆ m ʰa̲ngle s }

ˡs̲phereDistances : Num a => a -> Float

function · line 112

s̲phereDistances P: the great-circle distance between every pair of rows of P (points on the sphere): a matrix, 0 on its diagonal.

      "mf:" u̲se< "Manifold"
      ᵐᶠs̲phereDistances 2 2 r̲eshape 1.0 0.0 0.0 1.0
               0.0 1.5707963267948966
1.5707963267948966                0.0
ˡs̲phereDistances ← { P → (M, S) ← ʰa̲partPairs P◆ M ʰa̲ngle S }

ˡs̲phereTangent : (Num a, Num b) => a -> b -> Float

function · line 120

p s̲phereTangent v: the part of v that lies along the sphere at p (in the tangent plane there): v less its part along p, the normal. A tangent vector at p is one with no part along p.

      "mf:" u̲se< "Manifold"
      1.0 0.0 0.0 ᵐᶠs̲phereTangent 2.0 3.0 0.0
0.0 3.0 0.0
ˡs̲phereTangent ← { p v → (f̲loat v) − (p ʰd̲ot v) × f̲loat p }

ˡs̲phereStep : (Num a, Num b) => a -> b -> Float

function · line 132

p s̲phereStep v: the point reached from p by going along the sphere in the direction of the tangent vector v, for a distance equal to v's length (the exponential map): cos t p + sin t v / t with t = |v|, put back onto the sphere so no rounding takes it off. A zero v goes nowhere. (A quarter turn from the x axis along y lands on the y axis; the x left over is cos of a quarter turn, 0 up to the machine's rounding.)

      "mf:" u̲se< "Manifold"
      1.0 0.0 0.0 ᵐᶠs̲phereStep 0.0 1.5707963267948966 0.0
0.00000000000000006123233995736766 1.0 0.0
ˡs̲phereStep ← { p v →
  t ← ʰn̲orm v
  t = 0.0 ? f̲loat p
  ˡs̲phereProject ((c̲os t) × f̲loat p) + (s̲in t) × (f̲loat v) ÷ t
}

ˡs̲phereArrow : (Num a, Num b) => a -> b -> Float

function · line 146

p s̲phereArrow q: the tangent vector at p that s_phereStep carries to q (the logarithmic map): it points along the sphere toward q and is as long as the distance between them. For q = p it is zero; for q the opposite point, which every direction reaches at distance pi, it is zero too (there is no one arrow).

      "mf:" u̲se< "Manifold"
      1.0 0.0 0.0 ᵐᶠs̲phereArrow 0.0 1.0 0.0
0.0 1.5707963267948966 0.0
ˡs̲phereArrow ← { p q →
  u ← p ˡs̲phereTangent q
  n ← ʰn̲orm u
  n = 0.0 ? u
  (p ˡs̲phereDistance q) × u ÷ n
}
Used in: ˡs̲phereArc, v

ʰa̲rrows : (Num a, Num b) => a -> b -> Float

function (private) · line 155

The arrows at p to every row of P, one per row (s_phereArrow, over a set at once): the mean's one step.

ʰa̲rrows ← { p P →
  c ← (f̲loat P) '+ '× i̲nner f̲loat p
  U ← (f̲loat P) − c '× t̲able f̲loat p
  n ← s̲qrt '+ r̲/₂ U × U
  (m, s) ← P ʰa̲partRows p
  ((m ʰa̲ngle s) ÷ n m̲ax 1e-300) ʰs̲caleRows U
}

ˡs̲phereMean : Num a => a -> Float

function · line 175

s̲phereMean P: the mean of the rows of P on the sphere (the Frechet or Karcher mean): the point whose summed squared distances along the sphere to the rows is least. The plain mean of the rows lies inside the sphere; this starts from that mean put onto the sphere and takes 30 steps, each along the average of the arrows to the rows, which is a fixed point when the arrows cancel. Points spread over less than a hemisphere have one mean, and the steps reach it; rows whose plain mean is the zero vector (say, two opposite points) are refused.

      "mf:" u̲se< "Manifold"
      ᵐᶠs̲phereMean 2 3 r̲eshape 1.0 0.0 0.0 0.0 1.0 0.0
0.7071067811865476 0.7071067811865476 0.0
ˡs̲phereMean ← { P →
  n ← f̲loat f̲irst s̲hape P
  start ← ˡs̲phereProject ('+ r̲/ f̲loat P) ÷ n
  30 '{ p → p ˡs̲phereStep ('+ r̲/ p ʰa̲rrows P) ÷ n } p̲ower start
}

ˡs̲phereOff : Num a => a -> Float

function · line 189

s̲phereOff p: how far p is from the sphere: the difference between its length and 1, as a size. 0 for a point on it; what a test of a constraint reports. Given a set (a matrix), one per row.

      "mf:" u̲se< "Manifold"
      ᵐᶠs̲phereOff 0.0 0.5 0.0
0.5
      ᵐᶠs̲phereOff 2 2 r̲eshape 0.0 0.5 3.0 4.0
0.5 4.0
ˡs̲phereOff ← { p →
  2 = t̲ally s̲hape p ? a̲bs (s̲qrt '+ r̲/₂ (f̲loat p) × f̲loat p) − 1.0
  a̲bs (ʰn̲orm p) − 1.0
}

ʰc̲oordinate : Num a => Int -> a -> Float

function (private) · line 196

k c̲oordinate p: the k-th coordinate of a point, or the k-th column of a set of points (a matrix), as Floats.

ʰc̲oordinate ← { k p → 2 = t̲ally s̲hape p ? f̲loat k s̲elect₂ p◆ f̲loat k s̲elect p }

ʰc̲olumns : Int -> a -> a

function (private) · line 202

c̲olumns M: the columns of a (rows x n) matrix given as one vector of n columns laid end to end, back as the matrix: the transpose of the n x rows reshape. For writing a set of points coordinate by coordinate.

ʰc̲olumns ← { n v → o̲\ (n c̲at (t̲ally v) d̲iv n) r̲eshape v }

The sphere, drawn

ˡs̲pherePoints : Num a => a -> Float

function · line 217

s̲pherePoints LL: the point of the unit sphere at each (longitude, latitude) row of LL, in radians (longitude round the z axis from the x axis, latitude up from the equator): one row (x, y, z) per row of LL. These are the sphere's map: a parallel is rows of one latitude and many longitudes, a meridian rows of one longitude. (A quarter turn of latitude is the pole: its x is cos of a quarter turn, 0 up to the machine's rounding.)

      "mf:" u̲se< "Manifold"
      ᵐᶠs̲pherePoints 2 2 r̲eshape 0.0 0.0 0.0 1.5707963267948966
                               1.0 0.0 0.0
0.00000000000000006123233995736766 0.0 1.0
ˡs̲pherePoints ← { LL →
  lon ← 1 ʰc̲oordinate LL
  lat ← 2 ʰc̲oordinate LL
  3 ʰc̲olumns ((c̲os lat) × c̲os lon) c̲at ((c̲os lat) × s̲in lon) c̲at s̲in lat
}

ˡs̲phereArc : (Num a, Num b) => (a, b) -> Int -> Float

function · line 233

(p, q) s̲phereArc n: n + 1 points along the great circle from p to q (both on the sphere), p first and q last, evenly spaced along it: the points s_phereStep reaches from p along 0, 1/n, ..., 1 of the arrow to q. For q = p, or q the opposite point (no one arc), every point is p.

      "mf:" u̲se< "Manifold"
      (1.0 0.0 0.0, 0.0 1.0 0.0) ᵐᶠs̲phereArc 2
                               1.0                0.0 0.0
                0.7071067811865476 0.7071067811865475 0.0
0.00000000000000006123233995736766                1.0 0.0
ˡs̲phereArc ← { (p, q) n →
  v ← p ˡs̲phereArrow q
  t ← ʰn̲orm v
  rows ← (n + 1) r̲eshape 1.0
  t = 0.0 ? rows '× t̲able f̲loat p
  T ← (t ÷ f̲loat n) × (f̲loat r̲ange n + 1) − 1.0
  ʰp̲rojectRows ((c̲os T) '× t̲able f̲loat p) + (s̲in T) '× t̲able v ÷ t
}

The circle

ˡc̲irclePoints : Num a => a -> Float

function · line 254

c̲irclePoints A: the point of the unit circle at each angle of A (radians from the x axis, counterclockwise): one row (x, y) per angle. The circle is the sphere of one dimension, the unit sphere in two: s_phereProject puts a point onto it, s_phereDistance measures along it (the angle between two points), s_phereArc draws between them.

      "mf:" u̲se< "Manifold"
      ᵐᶠc̲irclePoints 0.0 3.141592653589793
 1.0                                0.0
-1.0 0.00000000000000012246467991473532
ˡc̲irclePoints ← { A → a ← f̲loat A◆ 2 ʰc̲olumns (c̲os a) c̲at s̲in a }

The torus

ˡt̲orusPoints : (Num a, Num b) => a -> b -> Float

function · line 268

radii t̲orusPoints UV: the point of the torus of radii R r (R from the middle of the hole to the middle of the tube, r the tube's) at each (u, v) row of UV, in radians (u round the hole, v round the tube): ((R + r cos v) cos u, (R + r cos v) sin u, r sin v), one row per row of UV. The torus's map is the (u, v) rectangle, 0 to 2 pi each way, each pair of opposite edges joined.

      "mf:" u̲se< "Manifold"
      3.0 1.0 ᵐᶠt̲orusPoints 2 2 r̲eshape 0.0 0.0 0.0 3.141592653589793
4.0 0.0                                0.0
2.0 0.0 0.00000000000000012246467991473532
ˡt̲orusPoints ← { radii UV →
  R ← f̲loat 1 s̲elect radii
  r ← f̲loat 2 s̲elect radii
  u ← 1 ʰc̲oordinate UV
  v ← 2 ʰc̲oordinate UV
  w ← R + r × c̲os v
  3 ʰc̲olumns (w × c̲os u) c̲at (w × s̲in u) c̲at r × s̲in v
}

ˡt̲orusOff : (Num a, Num b) => a -> b -> Float

function · line 286

radii t̲orusOff p: how far p is from the torus of radii R r: the distance from p to the circle through the middle of the tube, less r, as a size. 0 for a point on it. Given a set (a matrix), one per row.

      "mf:" u̲se< "Manifold"
      3.0 1.0 ᵐᶠt̲orusOff 0.0 0.0 0.0
2.0
      3.0 1.0 ᵐᶠt̲orusOff 2 3 r̲eshape 4.0 0.0 0.0 3.0 0.0 0.0
0.0 1.0
ˡt̲orusOff ← { radii p →
  R ← f̲loat 1 s̲elect radii
  r ← f̲loat 2 s̲elect radii
  x ← 1 ʰc̲oordinate p
  y ← 2 ʰc̲oordinate p
  z ← 3 ʰc̲oordinate p
  rho ← s̲qrt (x × x) + y × y
  a̲bs (s̲qrt ((rho − R) ^ 2.0) + z × z) − r
}

The Swiss roll

ˡr̲ollPoints : Num a => a -> Float

function · line 307

r̲ollPoints TH: the point of the Swiss roll at each (t, h) row of TH: (t cos t, h, t sin t). The roll is a flat sheet rolled up: along t it winds round the y axis, farther out with every turn (a spiral whose radius is t); along h it runs straight up the axis. Its map is the sheet itself, the rectangle of (r_ollAlong t, h).

      "mf:" u̲se< "Manifold"
      ᵐᶠr̲ollPoints 2 2 r̲eshape 0.0 0.0 3.141592653589793 1.0
               0.0 0.0                                0.0
-3.141592653589793 1.0 0.00000000000000038473413874435795
ˡr̲ollPoints ← { TH →
  t ← 1 ʰc̲oordinate TH
  h ← 2 ʰc̲oordinate TH
  3 ʰc̲olumns (t × c̲os t) c̲at h c̲at t × s̲in t
}

ˡr̲ollAlong : Num a => a -> Float

function · line 319

r̲ollAlong t: the length of the roll's spiral from its middle (t = 0) out to t, (t sqrt(1 + t^2) + asinh t) / 2: how far along the unrolled sheet the points at t lie. Its map's first coordinate.

      "mf:" u̲se< "Manifold"
      ᵐᶠr̲ollAlong 0.0 1.0
0.0 1.147793574696319
ˡr̲ollAlong ← { t → x ← f̲loat t◆ ((x × s̲qrt 1.0 + x × x) + ᵗʳa̲sinh x) ÷ 2.0 }

ˡr̲ollDistance : (Num a, Num b) => a -> b -> Float

function · line 329

a r̲ollDistance b: the distance along the sheet between the points of the roll at (t, h) a and b. The sheet is flat, so it is the plain distance between the two unrolled, sqrt((along a - along b)^2 + (h_a - h_b)^2): two points a turn apart are near in three dimensions and far along the sheet.

      "mf:" u̲se< "Manifold"
      0.0 0.0 ᵐᶠr̲ollDistance 1.0 0.0
1.147793574696319
ˡr̲ollDistance ← { a b →
  da ← (ˡr̲ollAlong 1 s̲elect a) − ˡr̲ollAlong 1 s̲elect b
  dh ← (f̲loat 2 s̲elect a) − f̲loat 2 s̲elect b
  s̲qrt (da × da) + dh × dh
}

The Poincare ball

ʰm̲obius : (Num a, Num b) => a -> b -> Float

function (private) · line 342

u m̲obius v: Mobius addition, the ball's own way of adding (u then v, each inside the unit ball): ((1 + 2 u.v + |v|^2) u + (1 - |u|^2) v) / (1 + 2 u.v + |u|^2 |v|^2). The result stays in the ball; (-u) m_obius v is v seen from u, as a vector at the middle.

ʰm̲obius ← { u v →
  uv ← u ʰd̲ot v
  uu ← u ʰd̲ot u
  vv ← v ʰd̲ot v
  (((1.0 + (2.0 × uv) + vv) × f̲loat u) + (1.0 − uu) × f̲loat v) ÷ (1.0 + (2.0 × uv) + uu × vv)
}

ˡb̲allDistance : (Num a, Num b) => a -> b -> Float

function · line 359

p b̲allDistance q: the distance between two points of the Poincare ball (the inside of the unit sphere with the hyperbolic metric; in two dimensions the Poincare disk), 2 atanh of the length of q seen from p, (-p) m_obius q. Near the middle it is twice the plain distance; toward the rim it grows without bound, so points near the rim a little apart in the plane are far apart along the ball. p and q must be inside the ball (length under 1).

      "mf:" u̲se< "Manifold"
      0.0 0.0 ᵐᶠb̲allDistance 0.5 0.0
1.0986122886681098
ˡb̲allDistance ← { p q → 2.0 × ᵗʳa̲tanh ʰn̲orm (n̲eg p) ʰm̲obius q }

ˡb̲allGeodesic : (Num a, Num b) => (a, b) -> Int -> Float

function · line 372

(p, q) b̲allGeodesic n: n + 1 points along the geodesic (the shortest path, by the ball's distance) from p to q, p first and q last, evenly spaced along it: p m_obius the arrow to q scaled by 0, 1/n, ..., 1 in the ball's way (tanh of that fraction of atanh its length). Drawn flat, a geodesic is an arc of a circle that meets the rim at right angles, or a diameter.

      "mf:" u̲se< "Manifold"
      (0.0 0.0, 0.6 0.0) ᵐᶠb̲allGeodesic 2
               0.0 0.0
0.3333333333333333 0.0
               0.6 0.0
ˡb̲allGeodesic ← { (p, q) n →
  w ← (n̲eg p) ʰm̲obius q
  r ← ʰn̲orm w
  rows ← (n + 1) r̲eshape 1.0
  r = 0.0 ? rows '× t̲able f̲loat p
  S ← (t̲anh (ᵗʳa̲tanh r) × ((f̲loat r̲ange n + 1) − 1.0) ÷ f̲loat n) ÷ r
  d ← t̲ally p
  (M, _) ← (n + 1) '{ (M, k) → (M c̲at (1 c̲at d) r̲eshape p ʰm̲obius (k s̲elect S) × w, k + 1) } p̲ower ((0 c̲at d) r̲eshape 0.0, 1)
  M
}

Distortion

ˡd̲istortion : (Num a, Num b) => a -> b -> Float

function · line 404

D d̲istortion E: how badly the distances E (between every pair of points of an embedding, a matrix as s_phereDistances gives) keep the distances D they should have (the same shape: the data's own distances, say along a tree): for every pair of different points, |E / D - 1|, the fraction by which the pair's distance is too long or too short; the mean over the pairs and the worst pair, as a pair of Floats. 0 0 when every distance is kept; 1 for a pair twice or half as far as it should be (half: 0.5). Pairs at D = 0 (the same point twice) are left out; the diagonal always is. The measure of H1 (docs/research-questions.md), the same whatever space E was measured along.

      "mf:" u̲se< "Manifold"
      D ← 3 3 r̲eshape 0.0 1.0 2.0 1.0 0.0 1.0 2.0 1.0 0.0
      D ᵐᶠd̲istortion D
0.0 0.0
      D ᵐᶠd̲istortion 2.0 × D
1.0 1.0
      D ᵐᶠd̲istortion 3 3 r̲eshape 0.0 1.0 1.0 1.0 0.0 1.0 1.0 1.0 0.0
0.16666666666666666 0.5
ˡd̲istortion ← { D E →
  n ← f̲irst s̲hape D
  pair ← ((r̲ange n) '< t̲able r̲ange n) ∧ (f̲loat D) > 0.0        ⍝ each pair once, where there is a distance to keep
  k ← '+ r̲/ r̲avel pair
  k = 0 ? 0.0 0.0
  off ← r̲avel (f̲loat pair) × a̲bs ((f̲loat E) ÷ (f̲loat D) m̲ax 1e-300) − 1.0
  (('+ r̲/ off) ÷ f̲loat k) c̲at 'm̲ax r̲/ off
}
Used in: distortion