Skip to content

CLI, so many bugfixes, block structured systems, mhom exposed, user homotopy, two-level parallelism...#238

Open
ofloveandhate wants to merge 224 commits into
bertiniteam:developfrom
ofloveandhate:develop
Open

CLI, so many bugfixes, block structured systems, mhom exposed, user homotopy, two-level parallelism...#238
ofloveandhate wants to merge 224 commits into
bertiniteam:developfrom
ofloveandhate:develop

Conversation

@ofloveandhate

Copy link
Copy Markdown
Contributor

No description provided.

ofloveandhate and others added 30 commits May 16, 2026 19:30
Stashed work from approximately 2026-05-04, validated locally at ~8.6%
speedup on test_endgames (serial). Held out of the 2.0.1 release; will
be polished and landed separately.

What changed:
- ExplicitRKPredictor: collapsed three PartialPivLU members and the
  LUSelector friends into a single std::tuple<PartialPivLU<dbl>,
  PartialPivLU<mpfr_complex>> LU_; all RK stages call .compute() into
  the persistent slot instead of constructing a fresh LU.
- ExplicitRKPredictor::FullStep: per-step Vec<ComplexType>(N) replaced
  with member step_temp_.
- ExplicitRKPredictor::SetNormsCond: per-call RandomOfUnits and solve
  result replaced with members rand_temp_ (filled once at ChangeSystem)
  and solve_temp_.
- NewtonCorrector::EvalIterationStep: J_temp_ref.lu() replaced with
  LU_ref.compute(J_temp_ref).
- NewtonCorrector::ChangePrecision: removed the wasteful fresh
  PartialPivLU construction.
- NewtonCorrector AMP Correct overloads: same rand_temp_ / solve_temp_
  pattern as the predictor.

Open items (not addressed here):
- StraightLineProgram::is_evaluated_ is a single bool spanning both
  dbl_complex and mpfr_complex memory; a dbl Eval can silently skip a
  subsequent mpfr Eval. Latent correctness issue.
- StraightLineProgram::precision() walks the entire memory block
  including temp slots; could skip temps.
- JIT/codegen mode for SLP::Eval — the compiler does most of the work
  already; would give a large dbl_complex speedup.
…shadowed base member

- MinimizeTrackingCost now filters out precisions where Criterion B
  requires a stepsize below min_stepsize, using double for the fast
  comparison and full RealT precision for the final value
- Remove dead num_successful_steps_since_precision_decrease_ declaration
  from base Tracker class; it was shadowed by the AMPTracker member and
  never initialized or used from the base

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…recision

Adds std::optional<unsigned> override_start_precision_ and
SetStartPrecision(std::optional<unsigned>) to AMPTracker, allowing
callers to start tracking at a specific precision regardless of the
start point's precision.  Pass std::nullopt (the default) to restore
the original behaviour of using the start point's precision.

Throws std::runtime_error if preserve_precision is on and the override
is lower than the start point's precision, since that combination would
silently downgrade the output point.

Also removes a redundant second ChangePrecision call in
TrackerLoopInitialization that was a no-op in the original code but
would have undone the override.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…calibration

Measures the relative cost of MPC complex arithmetic (GNU MPC via
boost::multiprecision) vs std::complex<double> at decimal-digit precisions
matching Bertini 2's convention (DoublePrecision=16, step=10).

Benchmarks three composite operations representative of path tracking:
dot product (SLP evaluation proxy), dense matvec, and LU factorization+solve.
Scalar loops are intentionally avoided -- the compiler eliminates them at -O2.

Results on this hardware give C(P) ≈ 101 + 1.59*P (decimal digits), vs the
stale paper values of 10.35 + 0.13*P from a 2009 Opteron 250.  The new
intercept is ~12x larger, reflecting modern AVX2 vectorization of double
that MPC cannot exploit.  ArithmeticCost() in amp_tracker.hpp still needs
updating once the methodology is finalized.

Compile with b2-ubuntu env:
  micromamba run -n b2-ubuntu g++ -O2 -std=c++17 \
      -I$CONDA_PREFIX/include -L$CONDA_PREFIX/lib -Wl,-rpath,$CONDA_PREFIX/lib \
      benchmarks/arithmetic_cost.cpp -o benchmarks/arithmetic_cost \
      -lmpfr -lgmp -lmpc

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace stale 2009 constants (10.35 + 0.13*P) with values measured on
modern hardware using benchmarks/arithmetic_cost.cpp:

  C(P) = 101.47 + 1.59 * P   (P in decimal digits)

The ~12x larger intercept reflects AVX2 vectorization of std::complex<double>
that GNU MPC cannot exploit.  The old formula underestimated multiprecision
cost by ~13x at P=20, causing the optimizer to prefer higher precision over
smaller steps almost unconditionally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…erionB; drop overly-strict SetStartPrecision guard

StepsizeSatisfyingCriterionB computed -(digits_B - precision) on unsigned
values. When precision < digits_B (the common case) the subtraction wrapped
to a huge unsigned value, so pow(10, huge) became +inf. That inf was capped
at max_stepsize, making low precisions look artificially cheap and biasing
MinimizeTrackingCost toward double precision regardless of problem
difficulty. Cast to int before negation so the exponent is computed
correctly.

Also remove the preserve_precision guard in SetStartPrecision that threw
when the override was below the start point's precision. preserve_precision
only guarantees the final point's precision matches the start point's; it
does not restrict the precision tracking begins at, so starting at any
user-requested precision is safe and the guard was overly strict.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tartPrecision cases

Six new cases in the AMP_tracker_basics suite:
- ArithmeticCost returns 1.0 at double precision and increases with precision
- MinimizeTrackingCost skips precisions whose criterion-B stepsize falls
  below min_stepsize, and throws when none satisfy it
- SetStartPrecision overrides to a higher precision, and clearing the
  override restores default behavior

These exercise the StepsizeSatisfyingCriterionB fix and the SetStartPrecision
behavior. All test_tracking_basics cases pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
manual_construction_x_times_lnum1_pow_num2l and
manual_construction_lx_pow_num2l_times_num1 compared a complex pow result
multiplied by another complex (magnitude ~120-130) against an absolute
tolerance of 1e-14. At that magnitude 1e-14 is finer than one ULP, so the
check demanded ~0.35x machine epsilon of relative accuracy and depended on
how libm pow rounds its last bit. The reference (unqualified pow) and the
node (std::pow) legitimately differ by ~1 ULP, which exceeds the absolute
bound; the relative error is ~2e-16, i.e. correct.

Switch the four dbl checks to the relative form already used elsewhere in
this file (fabs(value/exact - 1) < threshold), which is robust to libm
last-bit differences across toolchains. The mpfr checks are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Unify the inconsistent names used for the same numeric concepts into a
single self-documenting convention, across core/, tests, and the python
bindings:

  CT, ComplexType      -> ComplexT
  RT, RealType         -> RealT
  BaseComplexType      -> BaseComplexT
  BaseRealType         -> BaseRealT
  NumType              -> NumT   (canonical generic-numeric name)

Purely mechanical, behavior-preserving rename (whole-word substitution).
The full C++ suite (ctest, 10/10) passes and the library + _pybertini
module build cleanly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… Ref corruption

eigenpy's from-python converter for the writable Eigen::Ref<Vec<ComplexT>>
'result' argument of track_path_wrap corrupts boost.python's rvalue converter
storage for the adjacent scalar mpc_complex const& time arguments, leaving
start_time/end_time as garbage temporaries (invalid mpfr limb pointers). The
first use then trips MPFR set_prec.c:32 and aborts, which surfaced as the
test_tracker_* "MPFR precision-range assertion". This is a binding bug, not the
AMP precision/stepsize logic (the pre-branch binary aborts identically and the
C++ tracker tests pass).

Take the two time scalars by value instead of const&; the independent copy
side-steps the clobbered converter storage while the writable Ref still does the
in-place result write-back. Only live offender (other Eigen::Ref bindings are
single-arg). All four AMPTracker python tests now pass.

Also loosen parser_test test_create_system tolerance 1e-15 -> 1e-14: the
references are rounded magic constants and the division-based relative error sat
right at machine epsilon (actual miss 1.22e-15). Unrelated, pre-existing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ccache

target_sources(bertini2 PUBLIC ...) leaked every library .cpp into the
INTERFACE_SOURCES of the library, so all 10 test executables and bertini2_exe
recompiled the entire library themselves instead of linking it. The heaviest
TUs (mhom/system/total_degree/user, ~180-230s each) were each built into 12
targets. The block was pure redundancy -- those sources were already passed to
add_library -- so removing it makes each library source compile exactly once.

Removing the leak exposed a latent bug: the static libbertini2.a was non-PIC
but is linked into the shared _pybertini module. It only linked before because
each consumer (incl. _pybertini) recompiled the sources with -fPIC, so the
non-PIC archive members were never pulled. Mark the library
POSITION_INDEPENDENT_CODE ON to fix this properly.

Also add opt-in ccache support (USE_CCACHE, guarded by find_program; no-op when
ccache is absent) to speed up incremental/CI rebuilds.

Verified: full build links cleanly, ctest 10/10, wheel builds, python
unittest suite (66 tests) all pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…k binary

- dist/ : wheel/sdist output from python -m build
- .claude/ : local Claude Code config (settings.local.json, lock files)
- z_notes/ : personal working notes, not for the repo
- /benchmarks/arithmetic_cost : compiled benchmark binary (the .cpp stays tracked)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…& nag_algorithms

Add ConfiguredVisitor<OwnerT> -- one type-list-driven boost.python visitor that
exposes set_config/get_config/config_types on any Configured<...> owner, driven
by its own Config::UsedConfigs. Applied to every tracker and to the ZeroDim
algorithm (which previously had no Python config access at all); a new algorithm
gets the whole interface from one .def(ConfiguredVisitor<Algo>()) line.

Layer Pythonic ergonomics on top in bertini.config (introspection-driven, no
per-config/per-algorithm code): configs gain update()/to_dict/from_dict/repr/eq;
owners gain configure(**kwargs)/config_names(). Wired in via enhance_all/
enhance_owners in the tracking/endgame/nag_algorithm wrappers.

Un-ignore python/bertini/config.py (legacy autotools `config.*` rule was hiding
it from git and the scikit-build wheel). Add tracking/config_test.py (11 cases).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Flush out the empty NID skeleton into a real, configurable algorithm
framework wired to the reusable ConfiguredVisitor interface, plus Python
bindings and result datatypes. The regenerative cascade itself is not yet
implemented; Run/Solve/RegenerativeCascade are stubs that throw.

Core (header-only):
- Add a single-system management policy (SingleSysMgmtPolicy +
  policy::CloneTarget), the analog of CloneGiven for algorithms that do not
  form a homotopy from a start system.
- Rewrite numerical_irreducible_decomposition.hpp: forward decl, AlgoTraits
  with NeededConfigs = {Regeneration, Tolerances, Sharpening, PostProcessing},
  AnyNID base, derivation from Observable + CloneTarget + Configured, ctor +
  DefaultSetup, tracker/endgame getters, stubbed compute entry points.
- Fix the NID result datatype: give NonEmptyCodimensions() a real body (was
  non-void with empty body), and add NumWitnessSets()/GetWitnessSet(i) so the
  witness sets are reachable without a std::vector<WitnessSet> converter.

Python bindings:
- New numerical_irreducible_decomposition_export.{hpp,cpp}: NIDVisitor leads
  with ConfiguredVisitor, exports the 6-way matrix (PSEG/Cauchy x
  double/fixed-mp/AMP, no start system), and binds WitnessSet + the NID result
  for both dbl and mpfr -- all into the existing nag_algorithms submodule.
- Wire into CMakeLists, bertini_python.hpp, and call ExportNID() after
  ExportZeroDim() (call order matters: the shared configs are registered by
  ExportZeroDim and reused via the boost.python registry).

The Python wrapper needs no changes: enhance_all/enhance_owners over
nag_algorithms already gives NID configure()/config_names()/get_config/
set_config -- the payoff of the prior config-ergonomics work.

Verified: _pybertini builds; new python/test/nid suite 8/8; full python
suite green (classes 60, tracking 16, zero_dim 1, nid 8); C++
test_nag_algorithms + test_nag_datatypes pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a pure-Python convenience, bertini.variables(base, indices, fmt),
that builds a list of integer-indexed Variable nodes in one call instead
of constructing each by hand. indices accepts an int n (-> range(n)) or
any iterable of ints; fmt is a str.format template over {base}/{index}
(default '{base}{index}' -> x0, x1, ...). Returns a plain list, ready to
wrap in a VariableGroup.

No C++/binding changes -- Variable already constructs from a string name.

Re-exported at top level as bertini.variables; covered by four tests in
function_tree_test.py (count, iterable indices, custom format, compose).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eplace variable groups

Add ergonomic system-building, in C++ and exposed to Python:

- node::GatherVariables (new function_tree/gather.{hpp,cpp}): non-intrusive
  recursive descent collecting the distinct variables of a node/function
  list, alphabetically by name.
- System(std::vector<Fn>) constructor: auto-discovers variables into a
  single affine variable group.  Python: System([f0,f1,...]).
- System::RemoveVariable / FixVariable<T>: drop a variable from the solve
  set (keeping FIFO ordering consistent) and pin its value so the functions
  evaluate as if it were a constant.  Python: remove_variable / fix_variable.
- System::SetVariableGroups: replace the whole variable-group structure,
  preserving the path variable.  Python: set_variable_groups.
- SLPCompiler::Visit(Variable) now bakes a non-ordering variable as a
  constant from its current value (via the existing number machinery), so a
  fixed variable evaluates correctly under the default SLP evaluator and
  consistently with the function-tree evaluator.

Python bindings: list converters for vector<Function> and
vector<VariableGroup>, the new constructor/methods, and a
gather_variables free function.

Tests: 4 C++ cases in system_test.cpp and 4 Python cases in
system_test.py; full test_classes (443) and test_tracking_basics (87)
green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
test_system_Jac failed in isolation: the mpfr cases ran at the import-time
default of 20 digits, too coarse for the 1e-27 tolerances. It only passed when
an earlier suite (function_tree/differentiation, which set default_precision(30))
leaked global precision state. Make SystemTest self-contained, matching the
convention in the sibling Python suites and the C++ class tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…etrization)

Convert all 11 Python test suites from unittest.TestCase classes to plain
pytest functions and fixtures, and kill the cross-test global-state coupling
that made tests order-dependent (a test could pass in a full run yet fail in
isolation, e.g. the earlier test_system_Jac default-precision gap).

- conftest.py: autouse _reset_precision fixture restores the global default
  precision (DEFAULT_TEST_PRECISION=30) around every test, so no test inherits
  a neighbor's precision. Adds an overridable `precision` fixture.
- Remove the dead TextTestRunner aggregator scripts (test_classes/test_tracking/
  test_nid/test_zero_dim/test_all) that ran whole suites at import time.
- system_test: parametrize the mp eval/Jacobian cases across 30/50/80 digits
  with a precision-derived tolerance, replacing the hard-coded 1e-27.
- Drop per-setUp default_precision() calls (the fixture owns the baseline);
  translate assertions to bare asserts / pytest.raises.
- endgame_test: converted to a pytest function and marked xfail(strict=False) --
  it exposes a real, order/state-dependent AMP "MinimizeTrackingCost" bug the
  aggregators had been masking. Kept visible as work-to-do; to be fixed later.
- CLAUDE.md: document pytest as the single runner and the autouse precision
  baseline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ames, start systems, and zero-dim

Adds Python equivalents for C++ tests in tracking_basics, endgames,
start_system, system_class, function_tree, and zero_dim — covering
fixed/adaptive-precision trackers, all implemented predictors, PSEG and
Cauchy endgames at multiple precisions, TotalDegree start systems,
system/function-tree homogenization, and zero-dim solver settings.
RKF45+AMPTracker failure deferred as a known bug (tracked in memory).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… SuccessCode

AdjustAMPStepSuccess capped max_precision at max(min_precision, current_precision_),
collapsing the scan to a single candidate when digits_B exceeded current_precision_.
RKF45's larger error constant pushed digits_B >= 34 on the circle-line homotopy,
causing the scan to fail even though valid pairs existed at higher precision.

Primary fix: pass Get<PrecConf>().maximum_precision as the upper bound, matching
the paper (Bates et al. 2009 p. 8) which imposes no success-case ceiling.

Secondary fix: catch MinimizeTrackingCost's throw at both call sites
(AdjustAMPStepSuccess and AMPCriterionError) and return the new
SuccessCode::FailedToSelectPrecisionAndStepsize rather than propagating an
uncaught C++ exception into Python. AMPCriterionError changed from void to
SuccessCode; both TrackerIteration call sites updated accordingly.

- Adds SuccessCode::FailedToSelectPrecisionAndStepsize to enum and Python bindings
- Adds C++ regression test AMP_tracker_track_circle_line_RKF45
- Moves Predictor.RKF45 back into AMP_PREDICTORS in predictor_test.py
- Updates endgame_test xfail: MinimizeTrackingCost bug is fixed; remaining failure
  is a pre-existing CauchyEG precision mismatch (bdry_time copies source precision
  rather than current default_precision after the tracking loop resets state)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ime from Run()

EndgameBase now holds start_time_ and target_time_ (both BaseComplexT), set via
SetBoundaryTime()/SetTargetTime(). The single Run(Vec<BCT> const&) overload
re-precisions the stored times to match the incoming point before dispatching to
RunImpl(), so precision mismatches between time and point are impossible.

Fixes the long-standing xfail in test_using_total_degree_ss: AMPTracker would
leave bdry_points at a different precision than the mpfr_complex copy of
t_endgame_boundary, causing CauchyEG to throw 'Run time and point must be of
matching precision (50!=16)'.

Python wrapper (_EndgameBase) requires boundary_time at construction and exposes
run(point) only. C++ call sites in zero_dim_solve, generic_cauchy_test, and
generic_pseg_test updated accordingly. Python endgame tests updated to new API.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…espace

Lifts sin, cos, tan, asin, acos, atan, exp, log, and sqrt into the
bertini package so users can access them with a single import line
(`from bertini import *`). Adds sqrt as a free-function wrapper around
the Sqrt class, which was previously only callable as a constructor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…namespace

AbstractNode, AbstractSymbol, AbstractNamedSymbol, AbstractNumber, Handle,
and AbstractStartSystem are Boost.Python wrappers for C++ abstract base
classes.  Python users cannot construct or subclass them; they existed in
the public namespace only because the subpackage __init__.py files used
`__all__ = dir(<cpp_module>)`, which blindly re-exported everything.

Each affected __init__.py now dels the abstract names after the star import
and builds __all__ with an explicit exclusion set.  The C++ submodule
references for symbol, root, and start_system are also overridden with their
Python wrapper modules via importlib.import_module (a plain `from . import`
is short-circuited when the name is already defined by the star import).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three CI failures introduced by 2efdb4c:

1. Windows lld-link: CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS does not export private
   static data members of classes, so ExplicitRKPredictor Butcher-tableau
   constants (defined in the data-only explicit_predictors.cpp) were invisible
   to test executables linking against bertini2.dll. Fix: revert Windows to a
   static library (remove BUILD_SHARED_LIBS TRUE from the early WIN32 block)
   and add /WHOLEARCHIVE to each test-executable linker step so lld-link pulls
   every object, including the data-only one.

2. Stale manylinux wheel: cibuildwheel cache key did not hash
   tracker_export.hpp or endgame_export.hpp, so the by-value scalar fix in
   41bf852 may have been served from a pre-fix cached wheel. Added both
   python_bindings/include headers to the key.

3. ccache + clang-cl on Windows: add -DUSE_CCACHE=OFF to the Windows C++
   test cmake invocation as a precaution against ccache/clang-cl edge cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…kage

Replace `import bertini.foo` / `from bertini.foo import` with relative
equivalents (`from . import foo` / `from .foo import`) across all
python/bertini submodule __init__.py files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… choice

AlgoBuilder::ClassicBuild now applies all user-specified config values to the
constructed ZeroDim algorithm via a new virtual ApplyParsedConfigs(config_str).
Previously, parsed configs were discarded and the algorithm ran with
DefaultSetup() defaults regardless of what was in the input file.

- Add virtual AnyZeroDim::ApplyParsedConfigs; implement in ZeroDim using a
  C++17 fold-expression helper InjectParsedTuple that applies a parsed
  std::tuple<Ts...> to any Configured<>-derived target via Set<T>. Injects
  ZeroDim-owned configs, tracker configs (SteppingConfig/NewtonConfig/Predictor
  via Setup()), endgame configs (via AlgoTraits<EndgameType>::NeededConfigs),
  and MidPathConfig. MPI-ready: each rank calls the same method on the
  broadcast config_str.

- Add PrecisionType::FixedMultiple; fix Spirit parser (mptype:1 was
  incorrectly mapped to Adaptive); add three-way tracker dispatch in
  ClassicBuild. type::Tracker::FixedMultiple and ZeroDimSpecifyTracker
  dispatch were already correct.

- Add EndgameChoice enum + EndgameChoiceConfig struct (endgamenum: 1=PSEG,
  2=Cauchy); add ConfigSettingParser specialization; add to AllConfsD; wire
  dispatch in ClassicBuild. ZeroDimSpecifyEndgame dispatch was already correct.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds ENABLE_MPI=OFF cmake option. When ON, FindMPI locates OpenMPI/MPICH
(no Boost.MPI needed) and defines BERTINI2_HAVE_MPI.

New parallel infrastructure (all behind #ifdef BERTINI2_HAVE_MPI):
- parallel/mpi_utils.hpp: mpi_send/recv_serialized + mpi_broadcast_string
  bridging Boost.Serialization and plain C MPI
- parallel/path_result.hpp: PathBeforeEGResult<C>, Phase2Task<C>,
  PathDuringEGResult<C> with Boost.Serialization serialize() methods
- parallel/manager.hpp: RunManagerLoop<TaskT,ResultT> — dynamic assignment
- parallel/worker.hpp: RunWorkerLoop<TaskT,ResultT>

ZeroDim::Run() dispatches to RunParallel(WorldComm()) when Size() > 1.
RunParallel() runs two-phase manager-worker: before-EG then during-EG,
with EGBoundaryAction (midpath check) on rank 0 between phases.
Phase2Task carries boundary point+stepsize so workers are self-contained.

parallel::Initialize() checks MPI_Initialized() before MPI_Init for
mpi4py compatibility. Serial path is 100% unchanged; all tests pass.

Blackbox: rank 0 reads files and broadcasts config_str/input_str; rank-0-
only guards on file output writes.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
ofloveandhate and others added 8 commits June 16, 2026 06:11
…tegers

Add runnable Examples sections to the confusing helpers (coefficient, add_linear,
add_products_of_linears) and spell out how to get a non-integer EXACT coefficient
(exact decimal/rational string, fractions.Fraction, or a bertini.multiprec value),
since a Python float is refused.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Show the default (Cauchy/multiple/total-degree) and string selection, asserting
the resolved class name (deterministic, doctest-safe); the solve lines are +SKIP.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eprecation

- CMAKE_BUILD_PARALLEL_LEVEL 2 -> 4 for the Linux wheel build and the Linux C++
  test build (OS-conditional; macOS stays 2).  Justified by ADR-0019 (heaviest TU
  ~6.5 -> ~4 GB) and 16 GB Linux runners -- the two conditions ADR-0004 required.
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true: silences the Node 20 deprecation on
  conda-incubator/setup-miniconda@v3 (and other JS actions).
- auto-activate-base -> auto-activate (setup-miniconda input deprecation).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Python ZeroDim factory defaulted startsystem='totaldegree' regardless of the
system's variable structure -- but total degree THROWS ("more than one affine
variable group") on a multi-group system, and is the wrong (over-counting) start
for any multihomogeneous problem.  Default to startsystem='infer', mirroring the
C++ blackbox InferStartType: a single affine group with no projective groups ->
total degree; two-or-more groups, or any projective group -> multihomogeneous.
So ZeroDim(eigenvalue_system) now picks MHom automatically.  Explicit
'totaldegree' / 'mhom' still override.  Test covers 1-affine / 2-affine /
projective inference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…wn output rows

Record the load-bearing invariant uncovered by the Windows uninitialized-memory
bug (574ddb5): System allocates result buffers uninitialized, so each block must
write (and zero) every entry of its slice -- sparse contributors zero first.  This
is the motivation for folding the patch into a PatchBlock next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…from_var_groups

Epic set of improvements: 
Block-composed System: fold the polynomial path into blocks; MHom + linear-algebra on blocks.  See PR comments for fuller description.
reflects significant interface change and feature addition.  getting ready to merge upstream to bertiniteam
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment