UNPKG

yalps

Version:

Yet another linear programming solver. (A rewrite of javascript-lp-solver.) Aims to be decently fast.

285 lines (281 loc) 12.4 kB
/** Specifies the bounds for the total of a value. */ type Constraint = { /** * The total should be equal to this number. * In the case that `min` or `max` are also defined, this is used instead. */ readonly equal?: number; /** * The total should be greater than or equal to this number. * Can be specified alongside `max`. */ readonly min?: number; /** * The total should be less than or equal to this number. * Can be specified alongside `min`. */ readonly max?: number; }; /** * The coefficients of a variable represented as either an object or an `Iterable`. * `ConstraintKey` should extend `string` if this is an object. * If this is an `Iterable` and has duplicate keys, then the last entry is used for each set of duplicate keys. */ type Coefficients<ConstraintKey = string> = Iterable<readonly [ConstraintKey, number]> | ([ConstraintKey] extends [string] ? Readonly<Partial<Record<ConstraintKey, number>>> : never); /** * Indicates whether to maximize or minimize the objective. */ type OptimizationDirection = "maximize" | "minimize"; /** * The model representing a LP problem. * `constraints`, `variables`, and each variable's {@link Coefficients} can be either an object or an `Iterable`. * The model is treated as readonly (recursively) by the solver, so nothing on it is mutated. * * @typeparam `VariableKey` - the type of the key used to distinguish variables. * It should extend `string` if `variables` is an object. * * @typeparam `ConstraintKey` - the type of the key used to distinguish constraints, * the objective, and the coefficients on each variable. * It should extend `string` if `constraints` or any variable's {@link Coefficients} is an object. */ type Model<VariableKey = string, ConstraintKey = string> = { /** * Indicates whether to `"maximize"` or `"minimize"` the objective. * Defaults to `"maximize"` if left blank. */ readonly direction?: OptimizationDirection; /** * The key of the value to optimize. Can be omitted, * in which case the solver gives some solution (if any) that satisfies the constraints. * @example * Note that constraints can be placed upon the objective itself. * Maximize up to a certain point: * ``` * const model = { * direction: "maximize", * objective: "obj", * constraints: { obj: { max: 100 } }, * variables: [ * // ... * ] * } * ``` */ readonly objective?: ConstraintKey; /** * An object or `Iterable` representing the constraints of the problem. * In the case `constraints` is an `Iterable`, duplicate keys are not ignored. * Rather, the bounds on the constraints are merged to become the most restrictive. * @see {@link Constraint} * @example * Constraints as an object: * ``` * const constraints = { * a: { max: 7 }, * b: { equal: 22 }, * c: { min: 5 } * } * ``` * @example * Constraints as an `Iterable`: * ``` * const constraints = * new Map<string, Constraint>() * .set("a", { max: 7 }) * .set("b", { equal: 22 }) * .set("c", { min: 5 }) * ``` */ readonly constraints: Iterable<readonly [ConstraintKey, Constraint]> | ([ConstraintKey] extends [string] ? Readonly<Partial<Record<ConstraintKey, Constraint>>> : never); /** * An object or `Iterable` representing the variables of the problem. * In the case `variables` is an `Iterable`, duplicate keys are not ignored. * The order of variables is preserved in the solution, * but variables that end up having a value of `0` are not included in the solution by default. * @example * Variables as an object: * ``` * const variables = { * x: { a: 2, b: 11 }, * y: { a: 3, c: 22 } * } * ``` * @example * Variables as an `Iterable`: * ``` * const variables = * new Map<string, Coefficients>() * .set("x", { a: 2, b: 11 }) * .set("y", { a: 3, c: 22 }) * ``` * @example * Mixing objects and `Iterable`s: * ``` * const variables = { * x: { a: 2, b: 11 }, * y: [["a", 3], ["c", 22]] * } * ``` */ readonly variables: Iterable<readonly [VariableKey, Coefficients<ConstraintKey>]> | ([VariableKey] extends [string] ? Readonly<Partial<Record<VariableKey, Coefficients<ConstraintKey>>>> : never); /** * An `Iterable` of variable keys that indicate the corresponding variables are integer. * It is recommended to use a {@link Set} as the `Iterable`. * It can also be a `boolean`, indicating whether all variables are integer or not. * If this is left blank, then all variables are treated as not integer. */ readonly integers?: boolean | Iterable<VariableKey>; /** * An `Iterable` of variable keys that indicate the corresponding variables are binary * (can only be 0 or 1 in the solution). * It is recommended to use a {@link Set} as the `Iterable`. * It can also be a `boolean`, indicating whether all variables are binary or not. * If this is left blank, then all variables are treated as not binary. */ readonly binaries?: boolean | Iterable<VariableKey>; }; /** * This indicates what type of solution, if any, the solver was able to find. * @see `status` on {@link Solution} for detailed information. */ type SolutionStatus = "optimal" | "infeasible" | "unbounded" | "timedout" | "cycled"; /** * The solution object returned by the solver. */ type Solution<VariableKey = string> = { /** * `status` indicates what type of solution, if any, the solver was able to find. * * `"optimal"` indicates everything went ok, and the solver found an optimal solution. * * `"infeasible"` indicates that the problem has no possible solutions. * `result` will be `NaN` in this case. * * `"unbounded"` indicates a variable, or combination of variables, are not sufficiently constrained. * As such, the `result` of the solution will be +-`Infinity`. * `variables` in the solution might contain a variable, * in which case it is the unbounded variable that the solver happened to finish on. * * `"timedout"` indicates that the solver exited early for an integer problem. * This may happen if the solver takes too long and exceeds the `timeout` option. * This may also happen if the number of branch and cut iterations exceeds the `maxIterations` option. * In both of these cases, the current sub-optimal solution, if any, is returned. * If `result` is `NaN`, then this means no integer solutions were found before the solver timed out. * * `"cycled"` indicates that the simplex method cycled and exited. * This case is rare, but `checkCycles` can be set to `true` in the options to check for cycles and stop early if one is found. * Otherwise, if `maxPivots` (as set in the options) is reached by the simplex method, * then it is assumed that a cycle was encountered. * `result` will be `NaN` in this case. */ status: SolutionStatus; /** * The final, maximized or minimized value of the objective. * It may be `NaN` in the case that `status` is `"infeasible"`, `"cycled"`, or `"timedout"`. * It may also be +-`Infinity` in the case that `status` is `"unbounded"`. */ result: number; /** * An array of variables and their coefficients that add up to `result` while satisfying the constraints of the problem. * Variables with a coefficient of `0` are not included in this by default. * In the case that `status` is `"unbounded"`, `variables` may consist of one variable which is (one of) the unbounded variable(s). */ variables: [VariableKey, number][]; }; /** An object specifying the options for the solver. */ type Options = { /** * Numbers with magnitude equal to or less than the provided precision are treated as zero. * Similarly, the precision determines whether a number is sufficiently integer. * * The default value is `1e-8`. */ readonly precision?: number; /** * In rare cases, the solver can cycle. * This is assumed to be the case when the number of pivots exceeds `maxPivots`. * Setting this to `true` will cause the solver to explicitly check for cycles and stop early if one is found. * Note that checking for cycles may incur a small performance overhead. * * The default value is `false`. */ readonly checkCycles?: boolean; /** * This determines the maximum number of pivots allowed within the simplex method. * If this is exceeded, then it assumed that the simplex method cycled, * and the returned solution will have the `"cycled"` status. * If your problem is very large, you may have to set this option higher. * * The default value is `8192`. */ readonly maxPivots?: number; /** * This option applies to integer problems only. * If an integer solution is found within * `(1 +- tolerance) * {the problem's non-integer solution}`, * then this approximate integer solution is returned. * For example, a tolerance of `0.05` will return the first integer solution found within 5% of the non-integer solution. * This option is helpful for large integer problems where the most optimal solution becomes harder to find, * but approximate or near-optimal solutions may be much easier to find. * * The default value is `0` (only find the most optimal solution). */ readonly tolerance?: number; /** * This option applies to integer problems only. * It specifies, in milliseconds, the maximum amount of time * the main branch and cut portion of the solver may take before timing out. * If a time out occurs, the returned solution will have the `"timedout"` status. * Also, if any sub-optimal solution was found before the time out, then it is returned as well. * * The default value is `Infinity` (no timeout). */ readonly timeout?: number; /** * This option applies to integer problems only. * It determines the maximum number of iterations for the main branch and cut algorithm. * It can be used alongside or instead of `timeout` to prevent the solver from taking too long. * * The default value is `32768`. */ readonly maxIterations?: number; /** * Controls whether variables that end up having a value of `0` * should be included in `variables` in the resulting {@link Solution}. * * The default value is `false`. */ readonly includeZeroVariables?: boolean; }; /** * Returns a {@link Constraint} that specifies something should be less than or equal to `value`. * Equivalent to `{ max: value }`. */ declare const lessEq: (value: number) => Constraint; /** * Returns a {@link Constraint} that specifies something should be greater than or equal to `value`. * Equivalent to `{ min: value }`. */ declare const greaterEq: (value: number) => Constraint; /** * Returns a {@link Constraint} that specifies something should be exactly equal to `value`. * Equivalent to `{ equal: value }`. */ declare const equalTo: (value: number) => Constraint; /** * Returns a {@link Constraint} that specifies something should be between `lower` and `upper` (both inclusive). * Equivalent to `{ min: lower, max: upper }`. */ declare const inRange: (lower: number, upper: number) => Constraint; /** * The default options used by the solver. */ declare const defaultOptions: Required<Options>; /** * Runs the solver on the given model and using the given options (if any). * @see {@link Model} on how to specify/create the model. * @see {@link Options} for the kinds of options available. * @see {@link Solution} for more detailed information on what is returned. */ declare const solve: <VarKey = string, ConKey = string>(model: Model<VarKey, ConKey>, options?: Options) => Solution<VarKey>; export { type Coefficients, type Constraint, type Model, type OptimizationDirection, type Options, type Solution, type SolutionStatus, defaultOptions, equalTo, greaterEq, inRange, lessEq, solve };