java.lang.Object
com.darkcollective.relix.processor.eval.EquationSystemSolver

public final class EquationSystemSolver extends Object
Fills a row's unknown columns by solving SOLVE equations, and fits a group's (the engine side of SOLVE and SOLVE … PER).

The unknowns of a row are the participating columns that are NULL in it. A row is solved when there are exactly as many unknowns as equations, by the cheapest strategy that applies:

  1. a single equation whose unknown appears once is inverted by rearranging it (EquationSolver);
  2. equations linear in the unknowns — no product of two terms that both hold an unknown, no unknown in a divisor — are set up by evaluating each left − right with its gradient (Dual) and solved by elimination (LinearSystems); a singular system leaves the row unchanged;
  3. anything else is solved iteratively (Newton), which leaves the row unchanged when the equations do not determine the unknowns and raises when they have no solution or the search does not converge.
Any other row is returned unchanged.

Results are rounded to ten fractional digits, RoundingMode.HALF_UP, trailing zeros stripped — the precision of ÷.

The equations are expected in their planned form, with every user-defined function expanded. Instances are immutable and thread-safe.

  • Field Details

    • DEFAULT_TOLERANCE

      public static final BigDecimal DEFAULT_TOLERANCE
      The convergence tolerance when WITHIN is not given: 10⁻¹⁰.
    • DEFAULT_MAX_ROUNDS

      public static final int DEFAULT_MAX_ROUNDS
      The round cap when MAX … ROUNDS is not given.
      See Also:
  • Constructor Details

    • EquationSystemSolver

      public EquationSystemSolver()
      A solver with the default tolerance and round cap, inverting single equations with a default OperandEvaluator.
    • EquationSystemSolver

      public EquationSystemSolver(OperandEvaluator evaluator, BigDecimal tolerance, int maxRounds)
      A solver with the given iteration limits.
      Parameters:
      evaluator - the evaluator a single inverted equation's known side is read with; must not be null
      tolerance - the largest step an iteration may still take and be converged; positive
      maxRounds - the iteration's round cap; at least 1
  • Method Details

    • solve

      public Row solve(List<SolveEquation> equations, Row row)
      Returns row with its unknowns filled, or unchanged when it cannot be posed.
      Parameters:
      equations - the equations, every function expanded; must not be null
      row - the row to complete; must not be null
      Returns:
      the completed row, or row itself
      Throws:
      EvaluationException - on a non-numeric known value, a division by zero among the known terms, or an iteration that does not converge
    • fit

      public List<Row> fit(List<SolveEquation> equations, List<Row> group, String subject)
      Fits the equations' unknowns across a group of rows (the engine side of SOLVE … PER), returning every row of the group with the fitted values written in, or the group unchanged when it cannot be posed.

      The unknowns are the participating columns that are NULL in every row of the group; the observations are the rows in which every other participating column is present, each contributing one residual left − right per equation. The fit minimises the sum of the squared residuals and needs at least as many residuals as unknowns. Equations linear in the unknowns are fitted directly, by the normal equations; anything else iteratively (Newton), which raises when it does not converge. A fit that does not determine the unknowns — a singular system — leaves the group unchanged. With as many residuals as unknowns the fit is the exact solve.

      Parameters:
      equations - the equations, every function expanded; must not be null
      group - the rows of one group, all of one schema; must not be null
      subject - names the group in a diagnostic, e.g. material=steel
      Returns:
      the group's rows, in order, completed or unchanged
      Throws:
      EvaluationException - on a non-numeric known value, a division by zero among the known terms, or an iteration that does not converge