bertini.system¶
Provides utilities for working with systems of functions – polynomials are intended, although you can work with functions involving things like trig functions, arbitrary powers, etc.
Making a new System is the starting point you want, probably some of these things:
sys = bertini.system.System()
sys.add_function(...)
sys.add_variable_group(...)
x = sys.dehomogenize_point(z)
- class bertini.system.System((object)arg1)¶
Bases:
instanceThe type in Bertini for systems of simultaneous equations. Add functions and variable groups via member functions.
- __init__( (object)arg1, (bertini._pybertini.container.ListOfNode)functions) -> None :
Construct a System from a list of functions (bare expressions). The variables are auto-discovered from the functions and placed into a single affine variable group, ordered alphabetically by name.
- __init__((object)arg1) None¶
- __init__( (object)arg1, (bertini._pybertini.container.ListOfNode)functions) -> None :
Construct a System from a list of functions (bare expressions). The variables are auto-discovered from the functions and placed into a single affine variable group, ordered alphabetically by name.
- add(*objects)¶
Add functions and/or variable groups to the System, dispatched by type.
- Each argument may be:
a function-tree expression (e.g.
x**2 + y - 1, or a loneVariable) -> added as a function;a
VariableGroup-> added as an affine variable group;a numpy array / list / tuple of the above -> each element is added (so
sys.add(A @ x - lam*x),sys.add([f, g]), andsys.add(grp, f, g)work).
Projective groups still use
add_hom_variable_group(); structured blocks useadd_linear(). Returnsselffor chaining.
- add_function((System)self, (bertini._pybertini.function_tree.AbstractNode)f) None :¶
Add a function (a bare expression) to the System
- add_functions(expressions)¶
Add a vector/array of expressions as individual functions; returns the count added.
expressionsmay be a numpy object array, a (possibly nested) list, or a single expression node:sys.add_functions(A @ x - lam * x)
- add_hom_variable_group((System)self, (bertini._pybertini.container.VariableGroup)group) None :¶
Add a projective or homogeneous variable group to the System
- add_linear(A, x, b=None)¶
Add the linear conditions
A @ x + b == 0as one LinearFormsBlock.Ais an exact (m x n) coefficient matrix;xa length-n vector of the system’s variables (each already in a variable group);ban optional length-m exact constant vector (default zero). Coefficients must be exact. Returns self. Homogenization-aware.
- add_linear_forms(coefficients)¶
Add an affine-linear-forms block f(x) = M [x;1] (one LinearFormsBlock).
coefficientsis an (m x num_vars+1) array/list of EXACT values – one row per function, the trailing column being each form’s constant term. Python floats are refused. Returns self.
- add_linear_forms_block((System)self, (int)num_vars, (numpy.ndarray)coefficients) None :¶
Add a block of affine linear forms f(x) = M [x;1] to the System, evaluated as a single matrix-vector product rather than as scalar expressions. coefficients is an complex_mp matrix with one row per function and num_vars+1 columns; the trailing column carries each form’s constant term.
- add_path_variable((System)self, (bertini._pybertini.function_tree.symbol.Variable)pathvar) None :¶
Add a path variable to the System
- add_products_of_linears(factors)¶
Add a products-of-linear-forms block: f_i(x) = prod_r ( c_{i,r} . [x;1] ).
factorsis a list with one entry per function; entry i is an exact (k_i x num_vars+1) matrix (one row per linear factor, trailing column the factor’s constant). A product of k factors is one function of degree k. Coefficients must be exact. Returns self.
- add_products_of_linears_block((System)self, (int)num_vars, (list)factors) None :¶
Add a block of products-of-linear-forms f_i(x) = prod_r ( c_{i,r} . [x;1] ) to the System, evaluated as matrix-multiplies-then-row-products rather than as scalar expressions. factors is a list with one entry per function; entry i is an complex_mp matrix with one row per linear factor and num_vars+1 columns (the trailing column carries each factor’s constant term). Each function’s degree is its number of factors.
- add_slices_as_products(slices)¶
Add one products-of-linears function per slice (the regeneration bridge).
Each
Slicebecomes a single function that is the product of its linear forms. Slice coefficient columns must follow the system’s variable ordering. Returns self.
- add_variable_group(*variables)¶
Add an affine variable group – accepts several forms, unambiguously (issue #293).
All of these work (a single
Variable, aVariableGroup, and alistare mutually distinguishable, so there is no ambiguity):sys.add_variable_group(x, y, z) # loose variables sys.add_variable_group([x, y, z]) # a list sys.add_variable_group(x) # one variable -> a one-variable group sys.add_variable_group(bertini.VariableGroup([x, y, z])) # the explicit form
Returns
self.
- auto_patch((System)self) None :¶
Apply a patch to the system, given its current variable group structure.
- clear_variables((System)self) None :¶
Remove the variable structure from the system
- clone()¶
A copy of this System, ready to extend (issue #296).
Shares the immutable node DAG (variables, functions, subexpressions) with the original but gets its own evaluation memory, so its variables line up with the original’s (clone a set-up system, give the clone different functions, concatenate the two). The copy is unsealed and independently mutable; adding/removing functions on one does not affect the other. For a fully serialized deep copy use
copy.deepcopy.
- content_digest((System)self) str :¶
The persistent content digest of the system: SHA-256 of its canonical exact encoding, as 64 lowercase hex characters. Stable across sessions, machines, and versions of the encoding format (a format change bumps the version inside the encoding, changing digests loudly rather than silently). Everything evaluation-relevant is identity – functions, variable groups and ordering, path variable, patch and randomization coefficients, gamma; randomness included. Transient state (precision, current variable values, differentiation) is not. This is the key a database of solutions references systems and homotopies by.
- coordinates_of((System)self, (numpy.ndarray)point, (object)group) object :¶
Project a user-coordinate point onto one variable group: return just that group’s coordinates. group is either the VariableGroup object or its 0-based FIFO index. Affine groups return their affine coordinates; projective groups are returned as-is (not dehomogenized). Handy for an augmented system (e.g. a critical-point system) where you only care about one group.
coordinates_of( (System)self, (numpy.ndarray)point, (object)group) -> object
- copy_functions((System)self, (System)other) System :¶
Append another system’s functions to this one and return self (issue #297; sugar for add_functions(other.functions())).
- copy_patches((System)self, (System)other) None :¶
Copy the patches from another system into this one.
- copy_variable_structure((System)self, (System)other) None :¶
Copy the variable structure from another System
- degrees((System)self) bertini._pybertini.container.ListOfInt :¶
Get a list of the degrees of the functions in the system, with respect to all variables in all groups (and in fact overall)
- degrees( (System)self, (bertini._pybertini.container.VariableGroup)group) -> bertini._pybertini.container.ListOfInt :
Get a list of the degrees of the functions in the system, with respect to a variable_group passed in to this function. Negative numbers indicate non-polynomial
- dehomogenize_point((System)self, (numpy.ndarray)point) numpy.ndarray :¶
Dehomogenize a vector of doubles (complex), using the variable structure in this System
- dehomogenize_point( (System)self, (numpy.ndarray)point) -> numpy.ndarray :
Dehomogenize a vector of mpfr’s (complex), using the variable structure in this System
- describe((System)self[, (bool)verbose=False]) str :¶
A human-facing description of the system, block by block (the same as str(system) when verbose=False). verbose=True reveals the actual coefficients/matrices and the underlying functions of randomization / blend blocks. For reading, not re-parsing.
- differentiate((System)self) None :¶
differentiate the system with respect to the declared variable groups
- eval((System)self) numpy.ndarray :¶
Evaluate the system in multiple precision, using already-set variable values.
- eval( (System)self) -> numpy.ndarray :
Evaluate the system in double precision, using already-set variable values.
- eval( (System)arg1, (numpy.ndarray)self) -> numpy.ndarray :
Evaluate the system in multiple precision, using space variable values passed into this function.
- eval( (System)arg1, (numpy.ndarray)self) -> numpy.ndarray :
Evaluate the system in double precision, using space variable values passed into this function.
- eval( (System)arg1, (numpy.ndarray)arg2, (bertini._pybertini.multiprec.complex_mp)self) -> numpy.ndarray :
Evaluate the system in multiple precision using space and time values passed into this function. Throws if doesn’t use a time variable
- eval( (System)arg1, (numpy.ndarray)arg2, (complex)self) -> numpy.ndarray :
Evaluate the system in double precision using space and time values passed into this function. Throws if doesn’t use a time variable
- eval_jacobian((System)self) numpy.ndarray :¶
Evaluate the Jacobian (martix of partial derivatives) of the system, using already-set time and space value.
- eval_jacobian( (System)self) -> numpy.ndarray :
Evaluate the Jacobian (martix of partial derivatives) of the system, using already-set time and space value.
- eval_jacobian( (System)arg1, (numpy.ndarray)self) -> numpy.ndarray :
Evaluate the Jacobian (martix of partial derivatives) of the system, using space values you pass in to this function
- eval_jacobian( (System)arg1, (numpy.ndarray)self) -> numpy.ndarray :
Evaluate the Jacobian (martix of partial derivatives) of the system, using space values you pass in to this function
- eval_jacobian( (System)arg1, (numpy.ndarray)arg2, (complex)self) -> numpy.ndarray :
Evaluate the Jacobian (martix of partial derivatives) of the system, using time and space values passed into this function. Throws if doesn’t use a time variable
- eval_jacobian( (System)arg1, (numpy.ndarray)arg2, (bertini._pybertini.multiprec.complex_mp)self) -> numpy.ndarray :
Evaluate the Jacobian (martix of partial derivatives) of the system, using time and space values passed into this function. Throws if doesn’t use a time variable
- eval_time_derivative((System)self, (numpy.ndarray)space, (bertini._pybertini.multiprec.complex_mp)time) numpy.ndarray :¶
Evaluate dH/dt (the time derivative) in multiple precision at the given space and time values. Rows of t-independent blocks are zero.
- eval_time_derivative( (System)self, (numpy.ndarray)space, (complex)time) -> numpy.ndarray :
Evaluate dH/dt (the time derivative) in double precision at the given space and time values. Rows of t-independent blocks are zero.
- function((System)self, (int)index) bertini._pybertini.function_tree.AbstractNode :¶
Get a function with a given index. Problems ensue if out of range – uses un-rangechecked version of underlying getter
- functions((System)self) list :¶
The system’s functions, as a list of function-tree nodes (issue #297; structured blocks are expanded). So critpt_sys.add_functions(sys.functions()) copies them all in.
- get_patch((System)self) object :¶
Get (a reference to) the patches from the system.
- have_path_variable((System)self) bool :¶
Asks whether the System has a path variable defined
- hom_variable_groups((System)self) bertini._pybertini.container.ListOfVariableGroup :¶
Get the list of projective / homogeneous variable_groups from the system
- homogenize((System)self) None :¶
Homogenize the system, adding new homogenizing variables if necessary. This may change your polynomials; that is, it has side effects.
- homogenize_point((System)self, (numpy.ndarray)point) numpy.ndarray :¶
Take a point in user (dehomogenized) coordinates to this system’s internal coordinates: inserts the homogenizing coordinate for each affine variable group, then rescales onto the system’s patch if patched. Inverse of dehomogenize_point.
- homogenize_point( (System)self, (numpy.ndarray)point) -> numpy.ndarray :
Take a point in user (dehomogenized) coordinates to this system’s internal coordinates: inserts the homogenizing coordinate for each affine variable group, then rescales onto the system’s patch if patched. Inverse of dehomogenize_point.
- is_homogeneous((System)self) bool :¶
Determines whether all polynomials in the system have the same degree. Non-polynomial functions are not homogeneous.
- is_patched((System)self) bool :¶
Check whether the system is patched.
- is_polynomial((System)self) bool :¶
Determines whether all polynomials are polynomial. Transcendental functions, e.g., are non-polynomial. Returns a bool.
- is_same((System)self, (System)other) bool :¶
Content equality: True iff the two systems have equal content digests (identical canonical encodings). Independently built systems with the same mathematical content compare equal; systems differing in any identity-bearing way (functions, grouping, patch, gamma, …) do not. Does NOT change ==/hash semantics of the Python object.
- is_sealed((System)self) bool :¶
Whether the system has been sealed against structural mutation.
- jacobian(usercoordinates=True)¶
The symbolic Jacobian of the system, as a 2-D numpy object array of expression nodes.
J[i, j]is the partial derivative of functioniwith respect to variablej– an expression tree, not a number (contrasteval_jacobian(), which is numeric). Ready tonumpy.vstackonto a coefficient row and@a vector of variables.- Parameters:
usercoordinates (bool, default True) – When True, differentiate the functions as authored (the natural, pre-homogenization functions) with respect to the user-declared affine/projective variable groups: the solver-added homogenizing variables never appear and patches are omitted. When False, differentiate the functions as currently stored (possibly homogenized) with respect to the full internal variable ordering (homogenizing variables included), with the patch’s rows appended when the system is patched.
Notes
For a system that has already been homogenized, the user-coordinate Jacobian relies on the natural functions snapshotted at homogenization time. Build the system affinely and call
jacobianbefore homogenizing/solving for the cleanest result.
- num_functions((System)self) int :¶
The total number of functions in the system. Does not include patches.
- num_hom_variable_groups((System)self) int :¶
The number of homogeneous or projective variable groups. The number of homogenizing variables should eventually equal this.
- num_hom_variables((System)self) int :¶
The number of homogenizing variables defined in the system. Should be equal to the number of homvargroups
- num_ungrouped_variables((System)self) int :¶
The number of variables, not grouped into an affine or projective space
- num_variable_groups((System)self) int :¶
The number of affine variable groups. This should probably be renamed to num_affine_variable_groups
- num_variables((System)self) int :¶
the total number of variables in the system. Includes homogenizing variables
- precision((System)self) int :¶
Get the current precision of the system. Returns a postive number, representing the number of digits (not bits) at which the system is currently represented. (there is a reference-level precision stored, so you can change this up / down mostly fearlessly)
- precision( (System)self, (int)precision) -> None :
Set / change the precision of the system. Feed in a positive number, representing the digits (not bits) of the precision. Double precision is 16, but that only effects the multi-precision precision… you can eval in double precision without changing the precision to 16.
- randomization_matrix((System)self) numpy.ndarray :¶
The randomization matrix R (codimension x N, complex_mp) of a system produced by randomize(). Raises if the system has no randomization block.
- randomize(matrix=None, *, codimension=None)¶
Randomize a System, returning a NEW system (the original is not mutated).
With no arguments, an overdetermined System is squared down to its affine dimension. Pass
codimensionto instead randomize down to a chosen number of functions (sys.randomize(codimension=1)yields a single function); it must be a positive integer strictly less than the number of natural functions (randomization must reduce the count). Alternatively passmatrix, an exact coefficient matrix R (one row per randomized function, one column per natural function); its coefficients must be exact.codimensionandmatrixare mutually exclusive.
- reorder_functions_by_degree_decreasing((System)self) None :¶
Change the order of the functions to be in decreasing order
- reorder_functions_by_degree_increasing((System)self) None :¶
Change the order of the functions to be in decreasing order
- rescale_point_to_fit_patch((System)self, (numpy.ndarray)point) numpy.ndarray :¶
Return a rescaled version of the input point, which fits the patch for the system.
- rescale_point_to_fit_patch( (System)self, (numpy.ndarray)point) -> numpy.ndarray :
Return a rescaled version of the input point, which fits the patch for the system.
- rescale_point_to_fit_patch_in_place((System)self, (numpy.ndarray)point) None :¶
Re-scale the input point, in place, to fit the patch for the system. This assumes you have properly set the variable groups and auto-patched the system.
- rescale_point_to_fit_patch_in_place( (System)self, (numpy.ndarray)point) -> None :
Re-scale the input point, in place, to fit the patch for the system. This assumes you have properly set the variable groups and auto-patched the system.
- seal((System)self) None :¶
Seal the system: memoize its content digest and forbid structural mutation (hashcons-on-freeze). After sealing, structural mutators (add_function, homogenize, auto_patch, …) raise; evaluation, precision changes, and differentiation still work. Copying (clone / deepcopy) yields an unsealed copy. Idempotent.
- set_path_variable((System)self, (complex)values) None :¶
Set the value of the path variable. This one’s double-precision. Throws if path variable not defined.
- set_path_variable( (System)self, (bertini._pybertini.multiprec.complex_mp)values) -> None :
Set the value of the path variable. This one’s variable-precision. Throws if path variable not defined.
- set_variable_groups((System)self, (bertini._pybertini.container.ListOfVariableGroup)groups) None :¶
Replace the entire variable-group structure of the System with the given list of (affine) variable groups. Clears existing groups but preserves the path variable.
- set_variables((System)self, (numpy.ndarray)values) None :¶
Set the values of the variables. Expects a vector of doubles
- set_variables( (System)self, (numpy.ndarray)values) -> None :
Set the values of the variables. Expects a vector of complex mpfr’s
- slices((System)self) list :¶
The linear-form slices embedded in this system, one per linear-forms block (an empty list if none). Lets you back out the slice structure of a system that was built from a slice – the inverse of Slice.as_system().
- symbolic_jacobian((System)self[, (bool)usercoordinates=True]) list :¶
The symbolic Jacobian of the system, as a list of rows of expression nodes (NOT numeric – contrast eval_jacobian). usercoordinates=True (default): differentiate the natural (pre-homogenization) functions w.r.t. the user-declared affine/projective variable groups – homogenizing variables never appear, patches omitted. usercoordinates=False: differentiate the current (possibly homogenized) functions w.r.t. the full internal variable ordering (homogenizing variables included), with the patch’s rows appended when patched. Prefer bertini.System.jacobian(…), which returns a 2-D numpy object array.
- to_classic_input((System)self[, (int)mptype=2[, (int)odepredictor=5[, (float)tracktolbeforeeg=1e-05[, (float)tracktolduringeg=1e-06[, (float)finaltol=1e-11[, (float)maxstepsize=0.1[, (float)stepsuccessfactor=2.0[, (float)stepfailfactor=0.5[, (int)stepsforincrease=5[, (int)maxnumbersteps=100000[, (int)maxnewtonits=2[, (int)maxcrossedpathresolves=2]]]]]]]]]]]]) str :¶
Emit this system as a Bertini 1 classic input file (a CONFIG + INPUT string) so the same problem can be solved in Bertini 1 for cross-validation. Every tracking knob that governs path resolution – predictor, tolerances, and the FULL step-size cadence (maxstepsize / stepsuccessfactor / stepfailfactor / stepsforincrease) plus maxnewtonits – is settable, so the emitted file is fully controlled against a Bertini 2 solve (defaults mirror Bertini 2’s). mptype: 0 double, 1 fixed-multiple, 2 adaptive. odepredictor: 0 Euler, 2 RK4, 5 RKF45, 6 Cash-Karp. AMP coeff/degree bounds are derived from the system. Run bertini1 on the result in a SCRATCH dir (it writes many files into its CWD).
- variable_groups((System)self) bertini._pybertini.container.ListOfVariableGroup :¶
Get the list of (affine) variable_groups from the system
- variable_ordering((System)self) bertini._pybertini.container.VariableGroup :¶
The ordering of variables saying what each coordinate of a point in THIS system’s coordinates means. On your original system these are your variables; on a solver’s target_system() the homogenizing variables appear too.
- bertini.system.clone((System)self) System :¶
Copy a System. The copy shares the immutable node DAG (variables, functions, subexpressions) with the original but gets its own evaluation memory, so it is safe to evaluate concurrently AND its variables line up with the original’s – which is what lets you clone a set-up system, give the clone different functions, and concatenate the two (issue #256). Adding/removing functions on one does not affect the other. For a fully serialized deep copy use copy.deepcopy or pickle.
- bertini.system.concatenate((System)self, (System)other) System :¶
concatenate two Systems to produce a new one. Appends the second’s functions onto a copy of the first. The two must share variable ordering (cloning one from the other, or just reusing the same variables, guarantees this – variables are canonical by name). If exactly one is patched, the result takes that patch.
- bertini.system.intern_system((object)system) System :¶
Hash-cons a System: return the live SEALED representative with an equal content_digest() if one exists, else seal and register this one. Two equal systems interned in one session come back as the SAME object (s1 is s2). The representative is sealed – structural mutators raise; clone() it to get a mutable copy. The intern table holds systems weakly, so it never keeps them alive on its own.
- bertini.system.make_homotopy((System)target, (System)start[, (str)path_variable='t'[, (bertini._pybertini.function_tree.AbstractNode)gamma=None]]) System :¶
Form the gamma-trick straight-line homotopy H = (1-t)*target + gamma*t*start, with the path variable added. At t=1 the homotopy is gamma*start (so start’s solutions are its roots) and at t=0 it is target. When start carries a structured block (e.g. a products-of-linears start system) the two systems are combined with a blend block; otherwise node arithmetic is used. gamma=None generates a random rational gamma. Pair with nag_algorithm.user_homotopy to solve.
- bertini.system.make_moving_homotopy((System)fixed, (System)start_moving, (System)end_moving[, (str)path_variable='t'[, (bertini._pybertini.function_tree.AbstractNode)gamma=None]]) System :¶
Form a homotopy that moves ONLY the moving rows, leaving the fixed system evaluated once. H = [ fixed’s blocks ; (1-t)*end_moving + gamma*t*start_moving ]: the fixed equations (polynomial system + any static slices) stay as their own blocks (evaluated once, contributing zero to dH/dt) while only the moving rows slide. At t=1 the moving rows are gamma*start_moving, at t=0 they are end_moving. start_moving and end_moving hold just the moving rows and share fixed’s variable structure. The fixed rows come first, then the moving rows; build the matching target as fixed concatenated with end_moving. gamma=None generates a random rational gamma. Pair with nag_algorithm.user_homotopy to solve.
- bertini.system.simplify((object)self) None :¶
Perform all possible simplifications. Has side effects of modifying your functions, if held separately. Shared nodes between multiple systems may have adverse effects
- bertini.system.symbolic_jacobian((object)functions, (object)variables) list :¶
The symbolic Jacobian of a list of functions with respect to a list of variables: J[i][j] = d functions[i] / d variables[j], as a list of rows of expression nodes. Purely symbolic (no evaluation). Prefer bertini.jacobian(…), which returns a 2-D numpy object array.
Submodules
Start systems |