{"id":25340934,"url":"https://github.com/IanManske/YALPS","last_synced_at":"2025-10-29T10:31:24.631Z","repository":{"id":39648665,"uuid":"486407789","full_name":"Ivordir/YALPS","owner":"Ivordir","description":"Yet Another Linear Programming Solver. (A rewrite of javascript-lp-solver.) Aims to be decently fast.","archived":false,"fork":false,"pushed_at":"2024-03-22T20:00:58.000Z","size":6603,"stargazers_count":57,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-01-20T23:56:25.223Z","etag":null,"topics":["integer-programming","linear-programming","optimization"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Ivordir.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2022-04-28T01:38:19.000Z","updated_at":"2025-01-12T02:06:49.000Z","dependencies_parsed_at":"2024-02-29T21:36:11.364Z","dependency_job_id":null,"html_url":"https://github.com/Ivordir/YALPS","commit_stats":{"total_commits":52,"total_committers":4,"mean_commits":13.0,"dds":0.05769230769230771,"last_synced_commit":"516664f66c2a8cc8e2b1e59b2eb5adbddd81d34c"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ivordir%2FYALPS","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ivordir%2FYALPS/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ivordir%2FYALPS/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ivordir%2FYALPS/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Ivordir","download_url":"https://codeload.github.com/Ivordir/YALPS/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238805869,"owners_count":19533618,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["integer-programming","linear-programming","optimization"],"created_at":"2025-02-14T08:17:47.723Z","updated_at":"2025-10-29T10:31:24.626Z","avatar_url":"https://github.com/Ivordir.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# YALPS [![](https://badgen.net/npm/v/yalps)](https://www.npmjs.com/package/yalps) [![](https://badgen.net/npm/license/yalps)](https://github.com/IanManske/YALPS/blob/main/LICENSE) [![](https://deno.bundlejs.com/badge?q=yalps)](https://bundlejs.com/?q=yalps)\n\n## What is This (For)?\n\nThis is **Yet Another Linear Programming Solver (YALPS)**. It is intended as a performant, lightweight linear programming (LP) solver geared towards small LP problems. It can solve non-integer, integer, and mixed integer LP problems.\nWhile webassembly ports of existing solvers perform well, they tend to have larger bundle sizes and may be overkill for your use case. YALPS is the alternative for the browser featuring a small [bundle size](https://bundlephobia.com/package/yalps).\n\nYALPS is a rewrite of [jsLPSolver](https://www.npmjs.com/package/javascript-lp-solver). The people there have made a great and easy to use solver. However, the API was limited to objects only, and I saw other areas that could have been improved. You can check out [jsLPSolver](https://www.npmjs.com/package/javascript-lp-solver) for more background and information regarding LP problems.\n\nCompared to jsLPSolver, YALPS has the following differences:\n\n- More flexible API (e.g., support for Iterables alongside objects)\n- Better performance (especially for non-integer problems, see [Performance](#Performance) for more details.)\n- Good Typescript support (YALPS is written in Typescript)\n\nOn the other hand, these features from jsLPSolver were dropped:\n\n- Unrestricted variables (might be added later)\n- Multiobjective optimization\n- External solvers\n\n# Usage\n\n## Installation\n\n```\nnpm i yalps\n```\n\n## Import\n\nThe main solve function:\n\n```typescript\nimport { solve } from \"yalps\"\n```\n\nAlternate helper functions:\n\n```typescript\nimport { lessEq, equalTo, greaterEq, inRange } from \"yalps\"\n```\n\nTypes, as necessary:\n\n```typescript\nimport { Model, Constraint, Coefficients, OptimizationDirection, Options, Solution } from \"yalps\"\n```\n\n## Examples\n\nUsing objects:\n\n```typescript\nconst model = {\n  direction: \"maximize\" as const,\n  objective: \"profit\",\n  constraints: {\n    wood: { max: 300 },\n    labor: { max: 110 }, // labor should be \u003c= 110\n    storage: lessEq(400), // you can use the helper functions instead\n  },\n  variables: {\n    table: { wood: 30, labor: 5, profit: 1200, storage: 30 },\n    dresser: { wood: 20, labor: 10, profit: 1600, storage: 50 },\n  },\n  integers: [\"table\", \"dresser\"], // these variables must have an integer value in the solution\n}\n\nconst solution = solve(model)\n// { status: \"optimal\", result: 14400, variables: [ [\"table\", 8], [\"dresser\", 3] ] }\n```\n\nIterables and objects can be mixed and matched for the `constraints` and `variables` fields. Additionally, each variable's coefficients can be an object or an iterable. E.g.:\n\n\u003c!-- prettier-ignore-start --\u003e\n\n```typescript\nconst constraints = new Map\u003cstring, Constraint\u003e()\n  .set(\"wood\", { max: 300 })\n  .set(\"labor\", lessEq(110))\n  .set(\"storage\", lessEq(400))\n\nconst dresser = new Map\u003cstring, number\u003e()\n  .set(\"wood\", 20)\n  .set(\"labor\", 10)\n  .set(\"profit\", 1600)\n  .set(\"storage\", 50)\n\nconst model: Model = {\n  direction: \"maximize\",\n  objective: \"profit\",\n  constraints: constraints, // is an iterable\n  variables: { // kept as an object\n    table: { wood: 30, labor: 5, profit: 1200, storage: 30 }, // an object\n    dresser: dresser // an iterable\n  },\n  integers: true // all variables are indicated as integer\n}\n\nconst solution: Solution = solve(model)\n// { status: \"optimal\", result: 14400, variables: [ [\"table\", 8], [\"dresser\", 3] ] }\n```\n\n\u003c!-- prettier-ignore-end --\u003e\n\n## API\n\nThis is a stripped down version of YALPS's API. Use the JSDoc annotations / hover information in your editor for more extensive documentation.\n\n```typescript\ntype Constraint = {\n  equal?: number\n  min?: number\n  max?: number\n}\n\nconst lessEq: (value: number) =\u003e Constraint\nconst greaterEq: (value: number) =\u003e Constraint\nconst equalTo: (value: number) =\u003e Constraint\nconst inRange: (lower: number, upper: number) =\u003e Constraint\n\ntype Coefficients\u003cConstraintKey = string\u003e =\n  | Iterable\u003c[ConstraintKey, number]\u003e\n  | (ConstraintKey extends string ? { [key in ConstraintKey]?: number } : never)\n\ntype OptimizationDirection = \"maximize\" | \"minimize\"\n\ntype Model\u003cVariableKey = string, ConstraintKey = string\u003e = {\n  direction?: OptimizationDirection // defaults to `\"maximize\"` if left blank\n  objective?: ConstraintKey // the value to optimize\n\n  constraints:\n    | Iterable\u003c[ConstraintKey, Constraint]\u003e\n    | (ConstraintKey extends string ? { [key in ConstraintKey]?: Constraint } : never)\n\n  variables:\n    | Iterable\u003c[VariableKey, Coefficients\u003cConstraintKey\u003e]\u003e\n    | (VariableKey extends string ? { [key in VariableKey]?: Coefficients\u003cConstraintKey\u003e } : never)\n\n  /**\n   * An `Iterable` of variable keys that indicate the corresponding variables are integer.\n   * It can also be a `boolean`, indicating whether all variables are integer or not.\n   * If this is left blank, then all variables are treated as not integer.\n   */\n  integers?: boolean | Iterable\u003cVariableKey\u003e\n\n  /**\n   * An `Iterable` of variable keys that indicate the corresponding variables are binary\n   * (can only be 0 or 1 in the solution).\n   * It can also be a `boolean`, indicating whether all variables are binary or not.\n   * If this is left blank, then all variables are treated as not binary.\n   */\n  binaries?: boolean | Iterable\u003cVariableKey\u003e\n}\n\ntype SolutionStatus = \"optimal\" | \"infeasible\" | \"unbounded\" | \"timedout\" | \"cycled\"\n\ntype Solution\u003cVariableKey = string\u003e = {\n  /**\n   * `status` indicates what type of solution, if any, the solver was able to find.\n   *\n   * `\"optimal\"`: everything went ok, and the solver found an optimal solution.\n   * `\"infeasible\"`: the model has no possible solutions.\n   * `\"unbounded\"`: a variable, or combination of variables, are not sufficiently constrained.\n   * `\"timedout\"`: the solver exited early for an integer problem (due to the `timeout` or `maxIterations` options).\n   * `\"cycled\"`: the simplex method cycled and exited (this is rare).\n   */\n  status: SolutionStatus\n\n  /**\n   * The final, maximized or minimized value of the objective.\n   * It may be `NaN` in the case that `status` is `\"infeasible\"`, `\"cycled\"`, or `\"timedout\"`.\n   * It may also be +-`Infinity` in the case that `status` is `\"unbounded\"`.\n   */\n  result: number\n\n  /**\n   * An array of variables and their coefficients that add up to `result` while satisfying the constraints of the problem.\n   * Variables with a coefficient of `0` are not included in this by default.\n   */\n  variables: [VariableKey, number][]\n}\n\ntype Options = {\n  /**\n   * Numbers with magnitude equal to or less than the provided precision are treated as zero.\n   * Similarly, the precision determines whether a number is sufficiently integer.\n   * The default value is `1E-8`.\n   */\n  precision?: number\n\n  /**\n   * In rare cases, the solver can cycle.\n   * This is assumed to be the case when the number of pivots exceeds `maxPivots`.\n   * Setting this to `true` will cause the solver to explicitly check for cycles and stop early if one is found.\n   * The default value is `false`.\n   */\n  checkCycles?: boolean\n\n  /**\n   * This determines the maximum number of pivots allowed within the simplex method.\n   * If this is exceeded, then it assumed that the solver cycled.\n   * The default value is `8192`.\n   */\n  maxPivots?: number\n\n  /**\n   * This option applies to integer problems only.\n   * If an integer solution is found within\n   * `(1 +- tolerance) * {the problem's non-integer solution}`,\n   * then this approximate integer solution is returned.\n   * For example, a tolerance of `0.05` will return the first\n   * integer solution found within 5% of the non-integer solution.\n   * The default value is `0` (only find the most optimal solution).\n   */\n  tolerance?: number\n\n  /**\n   * This option applies to integer problems only.\n   * It specifies, in milliseconds, the maximum amount of time the solver may take.\n   * The default value is `Infinity` (no timeout).\n   */\n  timeout?: number\n\n  /**\n   * This option applies to integer problems only.\n   * It determines the maximum number of iterations for the main branch and cut algorithm.\n   * It can be used alongside or instead of `timeout` to prevent the solver from taking too long.\n   * The default value is `32768`.\n   */\n  maxIterations?: number\n\n  /**\n   * Controls whether variables that end up having a value of `0`\n   * should be included in `variables` in the resulting `Solution`.\n   * The default value is `false`.\n   */\n  includeZeroVariables?: boolean\n}\n\nconst defaultOptions: Required\u003cOptions\u003e\n\nconst solve: \u003cVarKey = string, ConKey = string\u003e(model: Model\u003cVarKey, ConKey\u003e, options?: Options) =\u003e Solution\u003cVarKey\u003e\n```\n\n# Performance\n\nWhile YALPS generally performs better than javascript-lp-solver, this solver is still geared towards small problems (hundreds of variables or constraints). For example, the solver keeps the full representation of the matrix in memory as a dense array. As a general rule, the number of variables and constraints should probably be a few thousand or less, and the number of integer variables should be a few hundred at the most. If your use case has large problems, it is recommended that you first benchmark and test the solver on your own before committing to using it. For very large and/or integral problems, a more professional solver is recommended, e.g. [glpk.js](https://www.npmjs.com/package/glpk.js).\n\nNevertheless, below are the results from some benchmarks comparing YALPS to other solvers. Each solver was run 30 times for each benchmark problem. A full garbage collection was manually triggered before starting each solver's 30 trials. The averages and standard deviations are measured in milliseconds. Slowdown is calculated as `mean / fastest mean`. The benchmarks were run on ts-node v10.9.1 and node v19.8.1. Your mileage may vary in a browser setting.\n\n\u003cpre\u003e\nMonster 2: 888 constraints, 924 variables, 112 integers:\n┌────────────┬────────┬────────┬──────────┐\n│  (index)   │  mean  │ stdDev │ slowdown │\n├────────────┼────────┼────────┼──────────┤\n│   YALPS    │ 53.95  │  2.25  │    1     │\n│  glpk.js   │ 116.19 │  3.2   │   2.15   │\n│ jsLPSolver │ 184.9  │ 10.43  │   3.43   │\n└────────────┴────────┴────────┴──────────┘\n\nMonster Problem: 600 constraints, 552 variables, 0 integers:\n┌────────────┬──────┬────────┬──────────┐\n│  (index)   │ mean │ stdDev │ slowdown │\n├────────────┼──────┼────────┼──────────┤\n│   YALPS    │ 1.85 │  1.28  │    1     │\n│  glpk.js   │ 4.78 │  1.07  │   2.58   │\n│ jsLPSolver │ 7.41 │  5.03  │    4     │\n└────────────┴──────┴────────┴──────────┘\n\nVendor Selection: 1641 constraints, 1640 variables, 40 integers:\n┌────────────┬────────┬────────┬──────────┐\n│  (index)   │  mean  │ stdDev │ slowdown │\n├────────────┼────────┼────────┼──────────┤\n│  glpk.js   │  61.3  │  1.35  │    1     │\n│   YALPS    │ 296.05 │  3.21  │   4.83   │\n│ jsLPSolver │ 404.31 │ 15.73  │   6.6    │\n└────────────┴────────┴────────┴──────────┘\n\nLarge Farm MIP: 35 constraints, 100 variables, 100 integers:\n┌────────────┬───────┬────────┬──────────┐\n│  (index)   │ mean  │ stdDev │ slowdown │\n├────────────┼───────┼────────┼──────────┤\n│  glpk.js   │ 6.24  │  1.04  │    1     │\n│   YALPS    │ 30.46 │  1.29  │   4.88   │\n│ jsLPSolver │ 58.28 │  2.17  │   9.33   │\n└────────────┴───────┴────────┴──────────┘\n\nAGG2: 516 constraints, 302 variables, 0 integers:\n┌────────────┬──────┬────────┬──────────┐\n│  (index)   │ mean │ stdDev │ slowdown │\n├────────────┼──────┼────────┼──────────┤\n│   YALPS    │ 1.6  │  0.6   │    1     │\n│ jsLPSolver │ 7.09 │  3.05  │   4.44   │\n│  glpk.js   │ 7.57 │  0.96  │   4.74   │\n└────────────┴──────┴────────┴──────────┘\n\nBEACONFD: 173 constraints, 262 variables, 0 integers:\n┌────────────┬──────┬────────┬──────────┐\n│  (index)   │ mean │ stdDev │ slowdown │\n├────────────┼──────┼────────┼──────────┤\n│  glpk.js   │ 2.42 │  0.5   │    1     │\n│   YALPS    │ 2.59 │  0.59  │   1.07   │\n│ jsLPSolver │ 5.35 │  1.25  │   2.21   │\n└────────────┴──────┴────────┴──────────┘\n\nSC205: 205 constraints, 203 variables, 0 integers:\n┌────────────┬───────┬────────┬──────────┐\n│  (index)   │ mean  │ stdDev │ slowdown │\n├────────────┼───────┼────────┼──────────┤\n│  glpk.js   │  2.6  │  0.5   │    1     │\n│   YALPS    │ 7.18  │  0.23  │   2.76   │\n│ jsLPSolver │ 10.86 │  1.69  │   4.17   │\n└────────────┴───────┴────────┴──────────┘\n\nSCFXM1: 330 constraints, 457 variables, 0 integers:\n┌────────────┬───────┬────────┬──────────┐\n│  (index)   │ mean  │ stdDev │ slowdown │\n├────────────┼───────┼────────┼──────────┤\n│  glpk.js   │  6.3  │  0.31  │    1     │\n│   YALPS    │ 20.67 │   1    │   3.28   │\n│ jsLPSolver │ 33.22 │  4.81  │   5.27   │\n└────────────┴───────┴────────┴──────────┘\n\nSCRS8: 490 constraints, 1169 variables, 0 integers:\n┌────────────┬────────┬────────┬──────────┐\n│  (index)   │  mean  │ stdDev │ slowdown │\n├────────────┼────────┼────────┼──────────┤\n│  glpk.js   │  18.1  │  1.54  │    1     │\n│   YALPS    │  56.8  │  1.08  │   3.14   │\n│ jsLPSolver │ 101.08 │  3.67  │   5.59   │\n└────────────┴────────┴────────┴──────────┘\n\nSCTAP2: 1090 constraints, 1880 variables, 0 integers:\n┌────────────┬───────┬────────┬──────────┐\n│  (index)   │ mean  │ stdDev │ slowdown │\n├────────────┼───────┼────────┼──────────┤\n│  glpk.js   │ 19.87 │  1.82  │    1     │\n│   YALPS    │ 49.98 │  2.39  │   2.51   │\n│ jsLPSolver │ 102.8 │ 12.74  │   5.17   │\n└────────────┴───────┴────────┴──────────┘\n\nSHIP08S: 778 constraints, 2387 variables, 0 integers:\n┌────────────┬───────┬────────┬──────────┐\n│  (index)   │ mean  │ stdDev │ slowdown │\n├────────────┼───────┼────────┼──────────┤\n│  glpk.js   │ 13.51 │  0.53  │    1     │\n│   YALPS    │ 17.86 │  1.75  │   1.32   │\n│ jsLPSolver │ 65.88 │ 10.59  │   4.88   │\n└────────────┴───────┴────────┴──────────┘\n\u003c/pre\u003e\n\nThe code used for these benchmarks is available under `benchmarks/`. Measuring performance isn't always straightforward, so take these synthetic benchmarks with a grain of salt. It is always recommended to benchmark for your use case. Then again, if your problems are typically of small size, then this solver should have no issue (and may be faster)!\n\n# Maintenance/Status\n\nThis package is still being maintained (i.e., bug fixes and security updates as necessary). However, no new features are planned or being worked on at this time.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FIanManske%2FYALPS","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FIanManske%2FYALPS","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FIanManske%2FYALPS/lists"}