Skip to content

LumiX 0.3.2

Latest

Choose a tag to compare

@tdelphi1981 tdelphi1981 released this 18 Oct 11:05
dab8971

Added

  • LXCanonicalModel.goal_deviations: for each relaxed goal, the names of its goal row family and of its positive and negative deviation families. Empty for a model without goals. schema_version stays 1.
  • lumix.disable_logging() removes the handler enable_logging() added and lets lumix records propagate again.
  • LXCPSATSolver.solve(..., scaling_factor=...) sets the grid of continuous variables for one solve; it is also reachable as LXOptimizer().use_solver("cpsat", scaling_factor=...); it must be an integer >= 1.

Fixed

  • An infinite or NaN coefficient (linear, quadratic, abs/min/max/product) was dropped silently by the noise filter, so a different model was solved. It now raises a ValueError naming the place and the variable instance. An infinite objective constant and variable bounds that no value satisfies ([inf, inf], NaN) raise as well. Open bounds (None, ±inf) are unchanged.
  • CP-SAT reported an unbounded integer model as feasible at its open-bound cap. When a variable sits on the cap of an optimal solve, LumiX now solves the LP relaxation of the model with GLOP; if it is unbounded, the model is reported unbounded.
  • OR-Tools/SCIP reported some unbounded MIPs as optimal with values near SCIP's infinity (objective -1.97e19). An optimum with a value of 1e18 or more, or an objective of 1e19 or more, is now reported unbounded.
  • CP-SAT's overflow error for a row with a huge value talked about a "common denominator of 1"; it now names the value and the 64-bit limit.
  • CPLEX solved every quadratic objective for the global optimum (optimality target 3), which gives no duals, so a convex QP had no shadow prices. The global target is now used only when CPLEX reports the objective is not convex.
  • A row of one NamedTuple type was narrowed by value against instances of another NamedTuple type with equal fields.
  • piecewise_points and LXLinearizerConfig accepted 0 or a negative number of segments; a function value that is not finite at a breakpoint was used as it is. Both now raise a ValueError.
  • LXNonLinearFunctions.log, sqrt and tan used sentinel values (±1e10, 0) outside their domain. They now raise a ValueError naming the domain.
  • LXIndicatorTerm accepted any sense string and failed later with a KeyError; it now raises a ValueError at construction.
  • A goal |x| <= 3 on a free variable could not be linearized (an equality on a convex term needs finite bounds). One-sided goals with nonlinear terms are relaxed one-sided, so the term stays convex.
  • solve_goal_programming(..., mode="weighted") ran the sequential mode: a string never equalled the enum. Strings are accepted in any case, and an unknown mode raises a ValueError.
  • LXWhatIfAnalyzer, the scenario comparison report and the scenario charts computed the percent change in three ways; the sign was wrong for a negative baseline. They now share one definition, (new - base) / |base| * 100.
  • LXSensitivityAnalyzer.get_binding_constraints() returned nothing without duals (MIPs, CP-SAT). Binding now means zero slack.
  • enable_logging("VERBOSE") attached its handler before failing, and records were printed twice when the root logger also had a handler.
  • Warnings raised from code run with python -c pointed at sys:1 instead of the caller's line. The three deepcopy warnings now point at the user's code too.
  • Visualization: a template given to LXVisualizationConfig was overwritten, and theme colours were never used; the dashboard summary and other fixed-height charts were resized to the default height; interactive=False had no effect; the "enable sensitivity analysis" hint sat on the wrong panel; an infeasible solution was titled "Objective: 0.00"; utilization percentages were drawn for >=, == and non-positive rows; plot_satisfaction_status(tolerance=0) used the default tolerance; waterfall and shadow-price colours ignored the objective sense.
  • Docs: the rational conversion guide showed outputs from another algorithm (355/113); the quickstart showed a non-optimal plan; example 04 required CPLEX and exited with code 0 on errors.

Behavior changes

  • binary() keeps bounds set before it (the model intersects them with [0, 1]); it used to reset them to [0, 1].
  • Rows are narrowed by value only when they have the type of the variable's instances or a built-in type.
  • get_binding_constraints() is slack-based, and threshold defaults to None (a threshold filters by |shadow price|).
  • LXWhatIfResult.delta_percentage is relative to |baseline|, so its sign changes for a negative baseline, and it is nan for a zero baseline (it was 0.0).
  • LXNonLinearFunctions helpers follow the linearizer config's pwl_num_segments and adaptive_breakpoints unless segments is given (each helper had its own fixed count, 20-50).
  • A one-sided goal with nonlinear terms has one deviation; the other is fixed to 0. Linear goals are unchanged.
  • The auxiliary families of a linearized add_quadratic are keyed by the index keys (key_i, key_j) instead of the canonical positions.
  • enable_logging() sets propagate = False on the lumix logger while its handler is attached.
  • LXVisualizationConfig.template and color_sequence are kept when given; when left out, they follow theme, so non-default themes now use their own colours.
  • Constraint utilization is shown only for <= rows with a positive right-hand side (other rows have no percentage).
  • CP-SAT reports unbounded instead of feasible when a variable reaches the open-bound cap and the model's LP relaxation is unbounded. An optimum that only lies beyond the cap (a large finite bound or row) stays feasible with a warning.
  • OR-Tools/SCIP reports unbounded instead of optimal when a value reaches 1e18 or the objective 1e19.
  • LXVisualizationConfig(template=...) defaults to None (filled from theme).
  • Charts that set their own height keep it: configure(height=...) sizes only charts without one.
  • The scenario comparison shows - / N/A instead of a percentage for an infeasible scenario or a zero baseline.
  • get_binding_constraints(threshold=t) with an explicit threshold also requires zero slack, so it can return fewer rows than before.
  • LXNonLinearFunctions.custom(adaptive=...) defaults to the config's adaptive_breakpoints (it was True).
  • pwl_num_segments and num_segments must be integers >= 1.
  • LXVariableSensitivity.is_at_bound uses the bounds the model solves with, so a binary at 0 or 1 is at its bound.
  • pct_change reports a change below 1e-9 percent as 0, so tables never print -0.0%.
  • The unused LXSolverInterface._candidates helper is removed.
  • Non-finite coefficients, objective constants and impossible bounds raise a ValueError.