Chained homotopies: provenance all the way back ############################################### I'm thinking of a chain of homotopies as a sequence of solves where each solve's start points are the previous solve's solutions. This is the pattern of parameter continuation, as well as lots of steps in the numerical cellular decomposition. Because solving can record where each of its paths started (see :doc:`../automatic_record_keeping/index`), a chain can be more than its final answers, namely a directed graph of points, so that any final solution can be walked back through every intermediate run to the very first start point. This tutorial builds a small chain from the family :math:`\{x^2 - a^2,\; xy - 1\}` for growing :math:`a`, and then reads its own records back with the navigation tools. The family is deliberately not all sunshine: each member has just **two** finite roots (:math:`(\pm a, \pm 1/a)`) but total degree four, so the first solve tracks four paths and two of them have nowhere finite to go — they head to infinity. Each is a result: it either converges to an infinite endpoint or is truncated near infinity (``diverged``); either way it is recorded, and only the finite solutions are carried forward, so those lineages simply end. The complete script is :download:`chained_homotopies.py <../../../../../examples/chained_homotopies.py>`: .. literalinclude:: ../../../../../examples/chained_homotopies.py :language: python :caption: examples/chained_homotopies.py How to chain ================================= The root of a chain is an ordinary solve. Every further link passes the homotopy you built and where its paths start: .. code-block:: python results.append(pb.solve(target, homotopy=straight_line_homotopy(target, previous), start=results[-1], seed=42)) ``start=`` accepts a prior :class:`~bertini.records.SolveResult` (or its solutions) — those points carry provenance, so the new run's records link back to them with ``point_ref`` references. Raw arrays work too: they are archived as a **given**, and provenance bottoms out at the data you supplied. To chain from a run recorded in *another session* — or by the command-line ``bertini2`` — read its endpoints with :func:`bertini.solutions_of`; they come back as points with provenance, ready to chain. Reading a chain back ====================== The navigation tools read the plain records — any directory, any producer, no solver objects: * :func:`bertini.runs` — one :class:`pandas.DataFrame` row per run: when, how many paths, which seed, which software wrote it. * :func:`bertini.tracks` — one row per tracked path: its verdict (``success`` / ``diverged`` / ``failed``), and where it started. Endpoint coordinates stay out of the table unless you pass ``coordinates=True`` — on a million-path audit you want the statuses, not the bytes. * :func:`bertini.provenance_graph` — the points as a :class:`networkx.DiGraph`, one edge per path, pointing from where it started to where it ended. Time flows along the edges. * :func:`bertini.provenance` — the walk for a single point: its chain of ``{'run', 'index'}`` hops, ending at a start label or a given. Visualizing a chain of homotopies ================================== :func:`bertini.plot_chain` draws the chain **left to right** — each run a column, paths flowing rightward from their origins (squares) through every run, colored by verdict (green success, orange diverged, red failed): .. image:: chain_progression.png :alt: paths flowing left to right through a four-run chain; two lineages end at the first run and two survive to the end :align: center Read it like a family tree. Four total-degree start points enter the first run; two of them go to infinity (converging to an infinite endpoint, or truncated as ``diverged`` — either way an answer, and shown coloured by that verdict). They have no outgoing edges — an infinite or diverged point is an endpoint of knowledge, not a start for more tracking — and only the two finite lineages are carried forward, run after run, to the final member. ``solve(start=...)`` does that filtering for you: it chains a result's *finite solutions*. The same discipline applies to points you would rather not continue for other reasons (a singular endpoint, say, spotted by its ``cycle_num``/condition number in :func:`bertini.tracks` or ``is_singular`` in the solver's metadata): what you pass as ``start=`` is what gets carried, and the graph records exactly what you chose. Large solves are visualizable, with settings to help the plot not get overwhelming. A run with more than ``max_paths_drawn`` paths (default 200) is not drawn path-by-path — the figure automatically aggregates to one node per run, with edge widths showing how many paths flow between runs, so a million-path chain renders instead of crashing your session. The same instinct applies to :func:`~bertini.provenance_graph`: pass ``runs=`` to restrict a big directory to the chains you care about before building a graph out of it.