Contents Menu Expand Light mode Dark mode Auto light/dark, in light mode Auto light/dark, in dark mode Skip to content
Bertini 3.3.1 documentation
Logo
Bertini 3.3.1 documentation
  • ๐Ÿ‘‹ Welcome to Bertini 2
  • ๐Ÿ”ฆ Tutorials
    • Formulating and solving systems
      • ๐ŸŒ Finding all the solutions
      • ๐ŸŽฏ Solving a system with ZeroDimSolver
      • ๐Ÿ“ A real point on every component (the Trott curve)
      • ๐ŸŽฏ Critical points by rank deficiency (a null vector, not a determinant)
      • ๐Ÿงฎ Eigenvalues by homotopy continuation
      • ๐Ÿ” Parameter homotopy: solve once, re-solve many times
      • ๐Ÿงฑ Build a system in pieces with clone and concatenate
      • ๐ŸŽฒ Randomize an overdetermined system
      • ๐Ÿชข Author your own start system: a product of linears
      • ๐Ÿงฎ Canonical ordering, and opting out
      • ๐Ÿ—ƒ๏ธ A dataframe of solutions: filtering and plotting with pandas
    • Manipulating solutions
      • โœ‚๏ธ Intro to slices
      • ๐Ÿ›ท Move a slice, hold the system fixed
    • Solver settings & multiprecision
      • โš™๏ธ Carrying settings across related solves
      • ๐Ÿชข Crossed paths: when two paths become one
      • ๐ŸŽš๏ธ When double precision is not enough
      • ๐Ÿ”ฌ Precision models: double, multiple, adaptive
    • Parallelism in Python
      • ๐Ÿš€ Solving at scale: many cores, many machines
      • โš—๏ธ A bistable reaction network, mapped in parallel
    • Observing, metadata, and more
      • ๐Ÿ‘€ Watching the paths: observers and path data
      • ๐ŸŽจ The continuation cartoon, from real data โ€” and seeing precision change
      • ๐Ÿ–ผ๏ธ The classic continuation cartoon, from real data
    • Performance and benchmarking
      • โฑ๏ธ Timing Bertini 1 vs Bertini 2
    • The record-keeping system
      • Automatic record keeping
      • Chained homotopies: provenance all the way back
    • Doing things manually
      • โ™ป๏ธ Evaluation of cyclic-\(n\) polynomials
      • ๐ŸŽฎ Using an endgame to compute singular endpoints
      • ๐Ÿ›ค Tracking to nonsingular endpoints
  • ๐ŸŒŸ Everyday API
  • Multiprecision numbers and NumPy
  • ๐Ÿ”Ž Full API reference
    • bertini.algorithms
      • bertini.algorithms.zerodim
    • bertini.config
    • bertini.endgame
    • bertini.logging
    • bertini.multiprec
    • bertini.nag_algorithm
    • bertini.operators
    • bertini.parallel
    • bertini.parse
    • bertini.random
    • bertini.records
    • bertini.symbolics
    • bertini.sympy_bridge
    • bertini.system
      • bertini.system.start_system
    • bertini.tracking
      • bertini.tracking.observers
        • bertini.tracking.observers.amp
        • bertini.tracking.observers.double
        • bertini.tracking.observers.multiple
    • bertini.windows_dll_manager
  • ๐Ÿ“š Bibliography
Back to top
View this page

โš™๏ธ Carrying settings across related solvesยถ

A real computation is rarely one solve. A numerical irreducible decomposition, a parameter sweep, a multi-stage pipeline โ€“ each runs many related solves, and you usually want the same tracking settings on every one of them. Re-typing tolerances and step controls for each solver is both tedious and a place for them to silently drift apart.

Bertini gives every config owner โ€“ every tracker and every solver โ€“ the same small interface for this. (Owning configs is a capability, not a class: a tracker is not a solver, but both carry configs, so both get these methods.)

Set fields by nameยถ

Every numeric config field accepts a string โ€“ the exact, noise-free way to write a number (0.05 the Python float is not \(1/20\); "0.05" is). And you can set fields right on the solver without naming which config struct they live in: update() routes each field to whichever config owns it.

x, y = bertini.Variable('x'), bertini.Variable('y')
system = bertini.System()
system.add_function(x*x + y*y - 1)
system.add_function(x + y)
system.add_variable_group(bertini.VariableGroup([x, y]))

solver = bertini.ZeroDimSolver(system)
solver.update(final_tolerance="1e-11",                 # -> TolerancesConfig
              max_num_crossed_path_resolve_attempts=3) # -> ZeroDimConfig

from bertini.nag_algorithm import TolerancesConfig, ZeroDimConfig
assert solver.get_config(TolerancesConfig).final_tolerance == 1e-11
assert solver.get_config(ZeroDimConfig).max_num_crossed_path_resolve_attempts == 3

A misspelled field, or one that lives on a different owner, raises immediately rather than silently doing nothing โ€“ max_step_size is a tracker setting, so the solver rejects it:

try:
    solver.update(max_step_size="0.05")     # that field is on the tracker, not the solver
    raise AssertionError("should have raised")
except AttributeError:
    pass

solver.get_tracker().update(max_step_size="0.05")   # set it where it lives
from bertini.tracking import SteppingConfig
assert solver.get_tracker().get_config(SteppingConfig).max_step_size == bertini.multiprec.real_mp("0.05")

Carry a whole bundleยถ

get_settings() hands you the ownerโ€™s entire configuration as a plain dict {name: config} โ€“ independent, picklable copies. set_settings() applies one back. So you configure once and stamp every subsequent solver with the same settings.

reference = bertini.ZeroDimSolver(system)
reference.update(final_tolerance="1e-11", newton_before_endgame="1e-6")
settings = reference.get_settings()
assert set(settings) == set(reference.config_names())     # one entry per config

# ... later, for each related solve ...
next_solver = bertini.ZeroDimSolver(system)
next_solver.set_settings(settings)
assert next_solver.get_config(TolerancesConfig).final_tolerance == 1e-11

The bundle is an ordinary picklable value, so it can be stored or sent to another process โ€“ the same settings then drive a workerโ€™s solves as drive the managerโ€™s.

import pickle
carried = pickle.loads(pickle.dumps(settings))
worker_solver = bertini.ZeroDimSolver(system)
worker_solver.set_settings(carried)
assert worker_solver.get_config(TolerancesConfig).final_tolerance == 1e-11

The settings are precision-agnosticยถ

A bundle built from one precision model applies unchanged to another. This is what lets a multi-stage workflow mix precision models โ€“ a quick double-precision pass, then an adaptive re-solve โ€“ while carrying one set of tracking tolerances through all of them.

tuned = bertini.ZeroDimSolver(system, mptype='double')
tuned.update(final_tolerance="1e-10")
bundle = tuned.get_settings()

for mptype in ('multiple', 'adaptive'):
    solver = bertini.ZeroDimSolver(system, mptype=mptype)
    solver.set_settings(bundle)                       # drops on cleanly, any precision model
    assert solver.get_config(TolerancesConfig).final_tolerance == 1e-10

By default set_settings() applies only the configs the target actually has and skips the rest, so a bundle also moves between different kinds of owner (a tracker has no solver-level tolerances). Pass strict=True to instead require every config in the bundle to be applicable.

from bertini import AMPTracker
tracker = AMPTracker(system)
tracker.set_settings(settings)             # silently skips the solver-only configs
try:
    tracker.set_settings(settings, strict=True)
    raise AssertionError("should have raised")
except KeyError:
    pass

Put together, the pattern for any multi-solve workflow is: tune one owner, ``get_settings()``, and ``set_settings()`` on each of the rest.

Complete exampleยถ

The whole tutorial as one runnable script โ€“ assemble nothing, just run it:

carrying_settings.pyยถ
"""Carrying settings across related solves -- Bertini 2 tutorial.

Tune one config owner, get_settings(), and set_settings() on each of the rest.
Run:  python carrying_settings.py
"""

import pickle

import numpy as np
import bertini
from bertini import AMPTracker
from bertini.nag_algorithm import TolerancesConfig, ZeroDimConfig
from bertini.tracking import SteppingConfig


def build_system():
    """The little system every section solves."""
    x, y = bertini.Variable('x'), bertini.Variable('y')
    system = bertini.System()
    system.add_function(x*x + y*y - 1)
    system.add_function(x + y)
    system.add_variable_group(bertini.VariableGroup([x, y]))
    return system


def set_fields_by_name(system):
    """Route fields by name onto whichever config owns them."""
    solver = bertini.ZeroDimSolver(system)
    solver.update(final_tolerance="1e-11",                 # -> TolerancesConfig
                  max_num_crossed_path_resolve_attempts=3) # -> ZeroDimConfig

    assert solver.get_config(TolerancesConfig).final_tolerance == 1e-11
    assert solver.get_config(ZeroDimConfig).max_num_crossed_path_resolve_attempts == 3

    # A field on a different owner raises immediately rather than doing nothing.
    try:
        solver.update(max_step_size="0.05")     # that field is on the tracker, not the solver
        raise AssertionError("should have raised")
    except AttributeError:
        pass

    solver.get_tracker().update(max_step_size="0.05")   # set it where it lives
    assert solver.get_tracker().get_config(SteppingConfig).max_step_size == bertini.multiprec.real_mp("0.05")

    return solver


def carry_a_whole_bundle(system):
    """get_settings() -> plain picklable dict; set_settings() stamps another solver."""
    reference = bertini.ZeroDimSolver(system)
    reference.update(final_tolerance="1e-11", newton_before_endgame="1e-6")
    settings = reference.get_settings()
    assert set(settings) == set(reference.config_names())     # one entry per config

    # ... later, for each related solve ...
    next_solver = bertini.ZeroDimSolver(system)
    next_solver.set_settings(settings)
    assert next_solver.get_config(TolerancesConfig).final_tolerance == 1e-11

    # The bundle is an ordinary picklable value -- store it or ship it to a worker.
    carried = pickle.loads(pickle.dumps(settings))
    worker_solver = bertini.ZeroDimSolver(system)
    worker_solver.set_settings(carried)
    assert worker_solver.get_config(TolerancesConfig).final_tolerance == 1e-11

    return settings


def settings_are_precision_agnostic(system, settings):
    """One bundle drops cleanly onto any precision model, and across owner kinds."""
    tuned = bertini.ZeroDimSolver(system, mptype='double')
    tuned.update(final_tolerance="1e-10")
    bundle = tuned.get_settings()

    for mptype in ('multiple', 'adaptive'):
        solver = bertini.ZeroDimSolver(system, mptype=mptype)
        solver.set_settings(bundle)                       # drops on cleanly, any precision model
        assert solver.get_config(TolerancesConfig).final_tolerance == 1e-10

    # By default set_settings() applies only the configs the target has and skips the rest.
    tracker = AMPTracker(system)
    tracker.set_settings(settings)             # silently skips the solver-only configs
    try:
        tracker.set_settings(settings, strict=True)
        raise AssertionError("should have raised")
    except KeyError:
        pass


def main():
    system = build_system()
    set_fields_by_name(system)
    settings = carry_a_whole_bundle(system)
    settings_are_precision_agnostic(system, settings)


if __name__ == '__main__':
    main()
Next
๐Ÿชข Crossed paths: when two paths become one
Previous
Solver settings & multiprecision
Copyright © 2015-2026, Bertini Team
Made with Sphinx and @pradyunsg's Furo
Built from 15aec06 on 2026-07-13 04:03 UTC.
On this page
  • โš™๏ธ Carrying settings across related solves
    • Set fields by name
    • Carry a whole bundle
    • The settings are precision-agnostic
    • Complete example