bertini.records

The casual face of the structured output directory: solve, save, load.

Every solve writes durable, plain-text records of what was computed (the directory explains itself: see its README.txt), and consults them before computing – so rerunning a script is always safe: instant if done, resuming if crashed, fresh if new. The records directory is ambient (BERTINI_RECORDS_DIR, else ./bertini_output); nobody is required to name it.

import bertini as pb sols = pb.solve(my_system, seed=42) # records + resumes automatically pb.save(sols) # or pb.save(“my favorites”, sols) pb.load(“my favorites”) # back, in any later session

Reading the records never requires bertini: JSON lines throughout – history/ (what was asked, when), results/ (one file per run: the computed paths and their metadata), definitions/ (the exact inputs, content-addressed). Power users keep the full solver-object API; these three verbs are sugar over it.

bertini.records.solve(system, seed=None, directory=None, mptype='adaptive', precision=None, endgame='cauchy', homotopy=None, start=None)[source]

Solve a polynomial system, recording and resuming automatically.

Ensure-answered semantics: the solve consults the ambient records directory first; paths already recorded for this exact ask (system + settings + seed) are taken from the records, and only the rest are computed. Rerun a crashed script and it finishes; rerun a finished one and it is instant.

Parameters:
  • system (System) – The target system (no path variable).

  • seed (int, optional) – The reproducibility seed. solve(sys, seed=42) means the same homotopy – the same gamma, start points, and patch – forever, on every machine. Omitted: an EFFECTIVE seed is derived for this solve and the solve runs under it, so the seed recorded in the run’s ask reproduces that run standalone – a mid-session solve does not silently depend on the session’s earlier draw history. (Consecutive seedless solves get distinct seeds, chained deterministically from set_random_seed’s master when one was set.)

  • directory (str, optional) – Records directory override; default is ambient (see records_dir).

  • homotopy (System, optional) – A homotopy you built (e.g. bertini.nag_algorithm.blend_homotopy()), for a CHAINED solve: its paths run from your start points at t=1 to system’s solutions at t=0. Requires start.

  • start (SolveResult or iterable of points, optional) – Where the paths start. A prior SolveResult (or its solutions) chains with full provenance – the records link every new endpoint back through the prior run, all the way to the beginning. Raw points (arrays) are archived as a given: provenance bottoms out honestly at data you supplied.

  • mptype (str) – The precision MODEL ('double' / 'multiple' / 'adaptive'), passed to bertini.nag_algorithm.ZeroDimSolver().

  • precision (int, optional) – The number of DIGITS, applied via bertini.default_precision. A string here is the deprecated old spelling of mptype and warns.

  • endgame (str) – Passed through to bertini.nag_algorithm.ZeroDimSolver().

Returns:

The finite solutions (as Solution points that remember their run) plus the run id – a claim ticket, safe to drop.

Return type:

SolveResult

bertini.records.save(*args, description='', directory=None)[source]

Save almost anything under a name: save(thing) or save(name, thing).

A SolveResult (or anything with .run_id and .solutions) is declared as results with full provenance; any JSON-able value (dict, list, number, string) is recorded inline. The declaration lands in history/ (the points themselves live in results/, referred to by {run, index}). A nameless save(thing) is auto-named by timestamp; re-saving a name replaces it (newest wins).

bertini.records.load(name=None, directory=None)[source]

Load saved results by name – the other half of save().

load("my favorites") returns that result (its points, annotations, provenance, or its inline value); load() returns the whole dict of everything saved, by name. Reads the plain records – declarations and annotations from history/, the referenced points from results/ – so this works in any later session, on any producer’s directory, and the same files are readable without bertini at all.

bertini.records.records_dir(path=None)[source]

Get (or set, by passing a path) the ambient records directory for this process.

Resolution when unset: the BERTINI_RECORDS_DIR environment variable, else ./bertini_output. Created on demand. Returns the resolved path as a string.

Ambient recording is ON BY DEFAULT for every solver in the process – ZeroDimSolver and HomotopySolver record here just like solve, with no code at all. Setting a path chooses WHERE: it is exported to BERTINI_RECORDS_DIR, which the solver classes read for their ambient attach. recording(False) is the off switch: while recording is off, nothing is exported and the off sentinel survives.

class bertini.records.Solution(coordinates, provenance=None, annotations=None)[source]

Bases: ndarray

A solution point: coordinates that remember where they came from.

Behaves exactly like the numpy array you expect (index it, print it, feed it to a solver), while carrying .provenance ({'run': ..., 'index': ...} – the recorded path that produced it) and .annotations invisibly. Arithmetic produces plain derived points: a computed combination is a new thing, and its provenance is honestly absent.

property real

The real parts – correct also for the multiprecision complex dtype.

property imag

The imaginary parts – correct also for the multiprecision complex dtype.

class bertini.records.SolveResult(answer, run_id, directory, num_recalled, solver)[source]

Bases: object

What solve returns: the typed answer plus a claim ticket on the recorded run.

A thin records decorator around the answer (a ZeroDimResult today; an NIDResult once numerical irreducible decomposition lands) – it adds the run id, records directory, and recall count. Iterating / indexing / len and solutions delegate to the answer’s finite solutions, so a SolveResult still behaves like the list of solutions it used to be.

Forgetting to capture it loses nothing – the records hold the truth; another solve of the same ask re-mints an equivalent result (recalled, not recomputed).

__init__(answer, run_id, directory, num_recalled, solver)[source]
answer

the typed answer

Type:

ZeroDimResult (or later NIDResult)

run_id

the recorded run’s id ({run, index} is a point reference)

Type:

str

directory

the records directory this run lives in

Type:

str

num_recalled

paths taken from the records instead of computed

Type:

int

property solutions

The finite solutions (delegates to answer.finite).

property solver

The underlying solver object (all_solutions, solution_metadata, …).

classmethod from_solver(solver, directory=None)[source]

Build a SolveResult from a solver on which solve() has already run.

Snapshots the typed answer (a ZeroDimResult, its finite solutions carrying records provenance) and reads the records ticket (run id, directory, recall count) straight off the solver. The bare solver records itself – it attaches an output directory and writes every path as it completes – so this needs nothing from solve(); it is exactly how both a bare solver.solve() and the solve() convenience produce the same result type.

Parameters:
  • solver (object) – A zero-dim / homotopy solver on which solve() has run.

  • directory (str, optional) – The records directory to record on the result; defaults to the solver’s own attached records path (records_path()) when it recorded, else None.