โ๏ธ The Flight Recorderยถ
Solving is the easy story to tell. The hard story is what the tracker goes through to get there โ and near a badly singular solution it goes through a lot. The Flight Recorder films one brutally hard path all the way to a highly singular endpoint and reads out the adaptive-precision trackerโs full telemetry, step by step.
The setup: a singular rendezvousยถ
The subject is the origin \((0,0)\), where two rose curves \(r = \sin(7\theta)\) and \(r = \sin(5\theta)\) (one randomly rotated so their only structured coincidence is at the origin) meet. That intersection has multiplicity 35 โ thirty-five homotopy paths pile into the same point โ and it is crankable: raise the rose degrees or tighten the accuracy demand and the singularity, and the trackerโs struggle, get arbitrarily worse.
On the left is the geometry: two roses whose petals all pass through the origin. On the right is the solve โ all 35 homotopy paths, each a different colour, wandering out through the complex plane from their start points (dots) and converging on the singular target (the ring). These are the pathsโ real-time trajectories, computed with the power-series endgame, which tracks radially toward \(t \to 0\) โ unlike the Cauchy endgame it does not sample circles, so each path is an honest single-valued descent onto the point with no loops to explain away:
def record_convergence(m=7, n=5, seed=2):
"""The system-level companion: a POWER-SERIES solve, whose endgame tracks radially toward t=0
(no Cauchy loops), so every singular path is an honest single-valued real-time descent onto the
singular point. Returns the rotation and each path's (abs_t, affine) trajectory."""
collector, meta, rotation = _solve(m, n, seed, 'powerseries')
trajectories, multiplicity = [], 1
for path in collector.series:
md = meta.get(path.path_index)
if md and md.is_singular:
trajectories.append((np.abs(path.times()), path.points()[:, 1:] / path.points()[:, 0:1]))
multiplicity = int(md.multiplicity)
return dict(m=m, n=n, rotation=rotation, multiplicity=multiplicity, trajectories=trajectories)
The Flight Recorder: one path, all instrumentsยถ
Now zoom to a single one of those paths and watch its instruments as it descends. Here we ask for
very tight final accuracy (final_tolerance = 1e-24):
Reading the instrumentsยถ
The endgame spiral (left). The Cauchy endgame samples a circle of shrinking radius around the singular endpoint; the solution winds as \(t \to 0\), which the log-radial plot unwinds into a rose of its own โ every tracker step, coloured red (far, \(|t|\approx 1\)) through to violet (\(|t|\approx 10^{-16}\), almost there).
Precision (top right) climbs a staircase \(16 \to 20 \to 30 \to 40\) digits as adaptive precision escalates into
mpfrโ double precision simply cannot deliver the accuracy the tight tolerance demands at this conditioning, so the tracker buys more digits when it must (and, being frugal, briefly drops back when it can).The condition number (middle right) blows up by fourteen orders of magnitude as the Jacobian degenerates on approach โ this is why the precision has to climb.
The step size (bottom right) sawtooths downward: grown when the going is easy, cut (red markers โ a rejected step) when the predictor overreaches. Thousands of ever-smaller steps are the price of getting cleanly onto a multiplicity-35 point.
Cycle number is not multiplicityยถ
The spiral is labelled cycle number 7, not 35 โ a distinction worth pinning down, because it is a natural thing to trip on. Multiplicity (35) is a property of the solution: how many paths merge there. The cycle number (7) is a property of one pathโs endgame: its winding, so the solution behaves like \(x(t) \sim t^{1/7}\) and it takes seven loops of \(t\) around \(0\) to close. Under that monodromy the 35 paths partition into cyclic groups โ a group of size \(c\) is \(c\) paths that cyclically permute, each with cycle number \(c\). Ask the solver and it is exactly consistent: all 35 paths report cycle 7, i.e. five groups of 7, and \(5 \times 7 = 35\). The endgame never has to wind 35 times; it winds 7, because this path lives in a 7-cycle.
How it is capturedยถ
Everything above is real, streamed from the pathโs own tracker. Solve with a
SolutionPathCollector attached (it puts a
PathDataCollector on every pathโs tracker), pick the
singular path that escalates precision the most, and read off its trajectory and its diagnostics โ
\(|t|\), condition number, precision, and step size at every step:
def record_hard_path(m=7, n=5, final_tolerance=1e-24, seed=2):
"""The per-path cockpit: a tight-tolerance CAUCHY solve (its spiral IS the point), returning the
richest singular path's telemetry (the one that escalates precision the most) plus its metadata."""
collector, meta, _ = _solve(m, n, seed, 'cauchy', final_tolerance)
P = collector.series[0].DIAGNOSTIC_COLUMNS.index('precision')
best = None
for path in collector.series:
md = meta.get(path.path_index)
if not (md and md.is_singular):
continue
dgn = path.diagnostics()
key = (int(dgn[:, P].max()), len(dgn)) # most precision, then most steps
if best is None or key > best[0]:
best = (key, path, md)
_, path, md = best
dgn = path.diagnostics()
return dict(m=m, n=n, final_tolerance=final_tolerance, path_index=int(path.path_index),
affine=path.points()[:, 1:] / path.points()[:, 0:1],
abs_t=dgn[:, 0], condition=dgn[:, 1], precision=dgn[:, 2], stepsize=dgn[:, 3],
cycle=int(md.cycle_num), multiplicity=int(md.multiplicity),
precision_digits=int(md.precision_digits), accuracy_digits=int(md.accuracy_digits))
The rest is instrument-panel plotting; see flight_recorder.py in full.
Note
Raster (PNG) showpiece, regenerated through tools/refresh_doc_artifacts.py; not a doctest.
Run it directly with python flight_recorder.py. It is deliberately expensive โ a tight
tolerance on a multiplicity-35 point is thousands of tracker steps in escalating precision โ so
it takes appreciably longer than a tutorial figure.