Code Monkey home page Code Monkey logo

jslpsolver's Introduction

jsLPSolver

A linear programming solver for the rest of us!

What Can I do with it?

You can solve problems that fit the following fact pattern like this one from this site.

On June 24, 1948, the former Soviet Union blocked all land and water routes through East Germany to Berlin. A gigantic airlift was organized using American and British planes to supply food, clothing and other supplies to more than 2 million people in West Berlin.

The cargo capacity was 30,000 cubic feet for an American plane and 20,000 cubic feet for a British plane. To break the Soviet blockade, the Western Allies had to maximize cargo capacity, but were subject to the following restrictions: No more than 44 planes could be used. The larger American planes required 16 personnel per flight; double that of the requirement for the British planes. The total number of personnel available could not exceed 512. The cost of an American flight was $9000 and the cost of a British flight was $5000. The total weekly costs could note exceed $300,000. Find the number of American and British planes that were used to maximize cargo capacity.

So How Would I Do This?

Part of the reason I built this library is that I wanted to do as little thinking / setup as possible to solve the actual problem. Instead of tinkering with arrays to solve this problem, you would create a model in a JavaScript object, and solve it through the solver's solve function; like this:

Install:

(in Node)

npm install javascript-lp-solver --save

(in browser through CDN)

<script src="https://unpkg.com/javascript-lp-solver/prod/solver.js"></script>

(webpack)

const webpack = require('webpack'); //to access built-in plugins

module.exports = {
        "mode": "development",
        "plugins": [
            new webpack.IgnorePlugin(/(fs|child_process)/),
        ]
}

Use:

var solver = require("./src/solver"),
  results,
  model = {
    "optimize": "capacity",
    "opType": "max",
    "constraints": {
        "plane": {"max": 44},
        "person": {"max": 512},
        "cost": {"max": 300000}
    },
    "variables": {
        "brit": {
            "capacity": 20000,
            "plane": 1,
            "person": 8,
            "cost": 5000
        },
        "yank": {
            "capacity": 30000,
            "plane": 1,
            "person": 16,
            "cost": 9000
        }
    },
};

results = solver.Solve(model);
console.log(results);

which should yield the following:

{feasible: true, brit: 24, yank: 20, result: 1080000}

What If I Want Only Integers

Say you live in the real world and partial results aren't realistic, too messy, or generally unsafe.

You run a small custom furniture shop and make custom tables and dressers.

Each week you're limited to 300 square feet of wood, 110 hours of labor, and 400 square feet of storage.

A table uses 30sf of wood, 5 hours of labor, requires 30sf of storage and has a gross profit of $1,200. A dresser uses 20sf of wood, 10 hours of work to put together, requires 50 square feet to store and has a gross profit of $1,600.

How much of each do you produce to maximize profit, given that partial furniture aren't allowed in this dumb world problem?

var solver = require("./src/solver"),
    model = {
        "optimize": "profit",
        "opType": "max",
        "constraints": {
            "wood": {"max": 300},
            "labor": {"max": 110},
            "storage": {"max": 400}
        },
        "variables": {
            "table": {"wood": 30, "labor": 5, "profit": 1200, "table": 1, "storage": 30},
            "dresser": {"wood": 20, "labor": 10, "profit": 1600, "dresser": 1, "storage": 50}
        },
        "ints": {"table": 1, "dresser": 1}
    }
    
console.log(solver.Solve(model));
// {feasible: true, result: 1440-0, table: 8, dresser: 3}

My problem is HUGE. Can I do this async or something?

Yes! Or something!

So its not truly async, but an instance of solver can be easily(?) put in an instance of a web worker.

worker.js

// n.b. Solver connects itself to the global 'self'
// if its available...
//
importScripts("/prod/solver.js");

onmessage = function(d){
    var results = solver.Solve(d.data);
    postMessage(results);
};

main.html

    var w = new Worker("./worker.js");

    w.onmessage = function(d){
        //
        // do something fun / exciting with our results!
        //
        console.log(d);
    }

    w.postMessage(lp_model);

How Fast Can It Go?

Random selection of problems of "some" size / interest:

-----------------
-----------------
LargeFarmMIP [ 100  variables, 35  constraints,  100  integers ]
jsLPSolver: 16.475ms


-----------------
-----------------
Monster Problem [ 552  variables, 600  constraints,  0  integers ]
jsLPSolver: 18.142ms


-----------------
-----------------
monster_II [ 924  variables, 888  constraints,  112  integers ]
jsLPSolver: 308.026ms


-----------------
-----------------
Fancy Stock Cutting Problem [ 31  variables, 5  constraints,  31  integers ]
jsLPSolver: 1.396ms


-----------------
-----------------
Vendor Selection [ 1640  variables, 1641  constraints,  0  integers ]
jsLPSolver: 1222.659ms


Neat! What else can I do with it?

API / Guide

Below is my first pass at describing the various parts of the model, what they do, and other miscellaneous options that might not be super intuitive.

As much as possible, I'm trying to make all of the options / functions accessible by changing the JSON model. To me (maybe incorrectly), it's easier to be able to just call one method to do everything based on the model its given instead of having to hit seperate functions exposed on the solver itself.

optimize

This tells the model (wait for it) what to optimize (minimize or maximize). Typically (honestly, always) the thing you're optimizing is an attribute of a variable. For example, profit might be a variable attribute you want to maximize. In this case, your model would look like this:

    {
        "optimize": "profit",
        "opType": "max",
    }

MULTI OBJECTIVE OPTIMIZATION: This is kind of a throwaway function I added because I needed it for something. I don't know if there's a better way to do this, or if it even makes sense, so please take this with a grain of salt.

Say you have a problem where you want to eat as much "bacon", "cheddar cheese", and "french fries" as possible. To do this, set the "optimize" attribute of the model like this:

    "optimize": {
        "bacon": "max",
        "cheddar cheese": "max",
        "french fries": "max"
    }

This will return a result where no single objective can be improved without hurting at least one other objective. It also returns the results of the "child" optimization problems

opType

This tells the solver how to optimize your problem. Acceptable options are "min" for minimize and "max" for maximize.

variables

These are the inputs of your problem. For the word problem:

How many chairs, tables, and desks do you need to produce given that a chair requires ...

...chairs, tables, and desks are your variables. You can assign attributes to the variables (size, cost, weight, etc) that you can use to constrain the problem.

On your model, your variables would look like this:

        "variables": {
            "table": {"wood": 30, "labor": 5, "profit": 1200, "storage": 30},
            "dresser": {"wood": 20, "labor": 10, "profit": 1600, "storage": 50}
        },

constraints

Real world problems don't allow you to use an unlimited number of resources (sad). In order to solve problems like

Maximize Profit...

where resources are limited; constraints come into play. Here is where you put them. (In a normal LP tableau, these are the inequalities).

Using the above example, say you had at most 300 units of wood, 110 units of labour, and 400 units of storage. To represent this in JSON format, you would set it up like this:

    "constraints": {
        "wood": {"max": 300},
        "labor": {"max": 110},
        "storage": {"max": 400}
    },

...where for the first constraint, "wood" is the attribute you're setting a constraint on with a "maximum" of 300 units used to solve the the problem. Other options for constraints are "min" (minimum) and "equal" (equal to).

options

This is a catch-all place to put additional options on the model for the Solver to work with in an attempt to not clutter the "core" of the model too much.

options.timeout (default: none)

This option is how many milliseconds you want to allow for the solver to try and solve the model you're running. You set it like this:

"options": {
    "timeout": 10000
}

N.B. currently, it only works for mixed-integer linear programs

options.tolerance (default: 0)

For large scale integer problems the solving process can take increasingly long. However, oftentimes the solution to these problems does not have to be the absolute best possible solution, but rather a solution relatively close to the optimal one. In these cases, a variable called tolerance can be specified in the model object. The value assigned to the tolerance variable states that the solver should stop the solution process when the best solution found is within {{options.tolerance}}% of the best theoretical objective value.

It is set up like this:

"options": {
    "tolerance": 0.05
}

options.exitOnCycles (default: true)

Exits when cycles detected

External Solver Integration

(n.b. this is still very much in progress and subject to change...)

Basically I want to be able to work with "professional-grade" solver libraries through jsLPSolver; without incorporating hard dependencies / binary builds / etc.

lpsolve

To use, incorporate the following onto your model:

    "external": {
        "solver": "lpsolve",
        "binPath": "C:/lpsolve/lp_solve.exe",
        "tempName": "C:/temp/out.txt",
        "args": [
            "-s2",
            "-timeout",
            240
        ]
    }

Basically, its doing the following:

  1. Convert your model to something lpsolve can use
  2. Saves your model to a temporary file (hence the tempName attribute)
  3. Runs everything through a command line (require("child_process").execFile) against the lpsolve executable (binPath) with whatever arguments you need (args)
  4. Scrubs the results
  5. Returns a JSON object with the results

jslpsolver's People

Contributors

amilajack avatar ancientswordrage avatar chrispahm avatar cxuesong avatar justinwolcottcompass avatar jwally avatar rasmusfonseca avatar stalinone avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jslpsolver's Issues

Question about Multi-Objective Optimization

Hey Guys,
Thanks so much for making this library accessible, it is a great help.

I had a question about your Multi-Objective Optimization solver.

I am trying to set up a scenario like this.

Say that you want to make an batch of Pudding.

You want the batch of Pudding to have a yield of 100g.

In your pudding you have the following three ingredients:
-chocolate { fat: 5g/100g, carbs: 0g, protein: 0g, chocolate: 1}

  • milk { fat: 0, carbs: 10g/100g, protein: 0, milk: 1}
    -eggs { fat: 0, carbs: 0g, protein: 13g/100g, eggs: 1}

Here are our constraints:

  1. We want to make 100g of pudding.
  2. We want the pudding to be 30% fat, 10%protein and 25% carb

How would we set up our model in such a way that allows you to enter a contraint that chocolate+ milk + eggs equals 100g?

const model = {
    "optimize": {
      // Do we need to add optimize here? If we don't what is the outcome?
    },
    "constraints": {
        'carb': { "equal": 25 },
        'fat': { "equal": 30 },   
        protein: { "equal": 10 },
        We want ... egg + chocolate + milk = 100
    },
    "variables": {
        "chocolate":  { fat: 5g/100g, carbs: 0g, protein: 0g, chocolate: 1},
        "milk": { fat: 0, carbs: 10g/100g, protein: 0, milk: 1},
        "egg": { fat: 0, carbs: 0g, protein: 13g/100g, eggs: 1}
    }
};

Note this example is probably not feasible value wise (as i just made it up) but i'm just curious about how to add the total value constraints of the 3 variables. I'm also curious if there is a way to constrain a variable using a min AND a mix?

THanks a TONNNNN.

Andrew

It should be able to have constraints on the attribute being optimized

I want solver to be able to handle a use pattern like this:

I want to use as few {{x}} as possible, but we need
at least {{y}} there because of regulations. What
is the least number of {{x}} I can get away with
using?

Right now, if an attribute has a constraint on it and is being optimized the constraint gets ignored. An easy work-around would be something like this:

  1. Check to see if the model has any constraints using the same attribute as the optimize property.
  2. For each variable in the model, add a new attribute (call it fake- or something) and give it the same value as the attribute being constrained
  3. add a new constraint named fake or something (see step 2). Copy the old constraint to this new one
  4. Delete the old constraint
  5. Solve
  6. Solve

Example in README

Very low priority issue...
Example should be changed to

var fs = require("fs"),
solver = require("./src/solver"),
model = {};

// Read Data from File
fs.readFile("./your/model/file", "utf8", function(e,d){
// Convert the File Data to a JSON Model
model = solver.ReformatLP(d);
// Solve the LP
console.log(solver.Solve(model)); // this line is changed
});

NPM version update

@JWally could you update the version that is on NPM please ? Maybe you want to wait for the PR from @Toffi-123, but the patches from the last few PRs are critical enough to bump it up.

Binary variables

Does this package handle binary variables?

I tried this in the Tableau format, but I'm not getting the expected result.

rawModel = [ "max: 21 x11 22.5 x21 22.5 x12 24.5 x22 23 x13 25.5 x23 1500 y1 2000 y2 3000 y3", "1 x11 1 x21 <= 425", "1 x12 1 x22 >= 400", "1 x13 1 x23 >= 750", "1 x11 1 x12 1 x13 >= 550", "1 x21 1 x22 1 x23 >= 450", "1 x11 1 x21 -425 y1 <= 0", "1 x12 1 x22 -400 y2 <= 0", "1 x13 1 x23 -750 y3 <= 0", "1 x11 >= 0", "1 x12 >= 0", "1 x13 >= 0", "1 x21 >= 0", "1 x22 >= 0", "1 x23 >= 0", "y1 = {0,1}", "y2 = {0,1}", "y3 = {0,1}", "int x11", "int x21", "int x12", "int x22", "int x13", "int x23", ];

Thank you for your help.

Speeding up calculations

Hello!

This isn't so much an issue with your awesome algo. It's more a question of efficiency and speeding things up a little: how would I go about speeding up a very large calculation?

I have raised a question on SO and have also created a jsfiddle. I'd be very eager to hear what you think.

http://stackoverflow.com/questions/29413453/speed-up-simplex-algorithm
http://jsfiddle.net/Guill84/qds73u0f/

I am relatively new to the world of mathematical optimisation -is there a way the problem I have defined can be broken down into smaller chunks or 'reframed' to speed up the calculations? Or is there a reason why it is slow? Perhaps it isn't slow at all given what I am trying to do.

Kind regards (and yours very humbly)
G.

Question about creating model

Hi,
I found jsLPSolver while searching for nodejs module which could solve our problem. It is similar to your "Monster Problem". We have some shops which sell shoes. Some of them get demands on shoes which are not on stock and must be transported from other shops. The cost of transport is constant and not proportional to amount (or, maybe there a small additional fee for every shoe packed on truck). I can't imagine, how to create the model.

I started with
[{
"name": "Transport problem",
"optimize": "cost",
"opType": "min",
"constraints": {
"shop1 demand article1": { "min": 5, "max": 5},
"shop1 demand article2": { "min": 2, "max": 2},
"shop2 demand article1": { "min":1, "max": 1},
"shop2 demand article2": { "min": 3, "max": 3},
"shop3 stock article1": { "max": 8},
"shop3 demand article2": { "min": 2, "max": 2},
"shop4 stock article1": { "max": 3},
"shop4 stock article2": { "max": 4},
"shop5 demand article1": { "min": 2, "max": 2 },
"shop5 stock article2": { "max": 4 }
},
"variables": {
}
}]
but how can I define the costs for transport?

Parsing lp formatted files

@JWally

I found this problem http://miplib.zib.de/miplib2010/ns1766074.php and I converted the mps file into an lp one with lp_solve (lp_solve -parse_only -fmps ns1766074.mps -wlp ns1766074.lp) since the comments in Reformat.js mention lp_solver (you will find the lp file of the instance and the js file I use to generate the model in the archive below).

The problem is that even though it goes through the parsing, the solution is not the expected one and the parser seems to consider everything as a variable (even the 'cXX:' in front of the constraints). Is it possible for you to adapt the parser ? Because mps files seem to be popular (and there is just a command to go from mps to lp).

Archive.zip

Multi-objective solver inifinite loop?

Hi @JWally , excellent library! Thank you so much for creating this, I really like your approach to specifying problems. Much more readable than tableaus.
I have an example where I think the multi-objective solver has a very weird lockup that probably shouldn't be happening. As far as I was able to debug it, it solves all the variations of the program correctly, and then when it tries to solve the "cheater" model in the end to find the midpoints, it seems to run forever. I was wandering if you are interested in this and want me to provide some sample data to reproduce this?
Thanks!

Feature Documentation

@bchevalier,

Would you be up for helping me better document this library's functionality?
No rush at all, but I haven't done a great job of documenting what is in this library or how to use it very well. I figure we can kick around what all to include in this thread until we're comfortable moving over pieces to the readme.

For instance, I think you've made it possible to have free-variables (no 0 floor), but I'd have to dig in the tests on how to use it.

So here's my first pass at an outline:

  • What is the library, what does it do?
  • Explanation of model parts (opType, variables, int, etc)
  • Function API
  • (Alt) Input Styles
    • Object Model
    • Text File
    • Array
  • Other Functionality
  • Examples (?)

Again, I'm in no rush to get this done, but I think a good documentation would be useful.
Thoughts?

MILP Issues

@bchevalier and @lvenerosy

I've been trying to figure this out for about a week now, can't, and I'm ready to admit I need help; lol.

So the following problem is correctly solved at the following commit:
f36a25a443550c793d89a2afad3aec68904941bc

but produces a non-sense result with this one:

a7039daec6dea0156ce5b7bfd0c611f3fef2a997.

problem

{
    "name": "Taco Party",
    "optimize": "cost",
    "opType": "min",
    "constraints": {
        "people_served": {
          "min": 25 
        }
    },
    "variables": {
        "nachos": {
          "nachos": 1,
          "people_served": 21,
          "cost": 75
        },
        "tacos": {
          "tacos": 1,
          "people_served": 26,
          "cost": 90
        },
        "burritos": {
          "burritos": 1,
          "people_served": 31,
          "cost": 105
        },
        "fajitas": {
          "fajitas": 1,
          "people_served": 36,
          "cost": 120
        }
    },
    "ints": {
        "nachos": 1,
        "tacos": 1,
        "burritos": 1,
        "fajitas": 1
    }
}

result from old commit

{ feasible: true, result: 90, tacos: 1 }

result from new commit
{ feasible: true, result: 120, tacos: -0, fajitas: 1 }

detail from old commit

MilpSolution {
  feasible: true,
  evaluation: 90,
  _tableau: 
   Tableau {
     model: 
      Model {
        tableau: [Circular],
        name: undefined,
        variables: 
         [ Variable { id: 'nachos', cost: 75, index: 1, value: 0, priority: 0 },
           Variable { id: 'tacos', cost: 90, index: 2, value: 1, priority: 0 },
           Variable { id: 'burritos', cost: 105, index: 3, value: 0, priority: 0 },
           Variable { id: 'fajitas', cost: 120, index: 4, value: 0, priority: 0 } ],
        variableIds: [ <1 empty item>, 'nachos', 'tacos', 'burritos', 'fajitas' ],
        integerVariables: 
         [ Variable { id: 'nachos', cost: 75, index: 1, value: 0, priority: 0 },
           Variable { id: 'tacos', cost: 90, index: 2, value: 1, priority: 0 },
           Variable { id: 'burritos', cost: 105, index: 3, value: 0, priority: 0 },
           Variable { id: 'fajitas', cost: 120, index: 4, value: 0, priority: 0 } ],
        unrestrictedVariables: {},
        constraints: 
         [ Constraint {
             index: 0,
             model: [Circular],
             rhs: 25,
             isUpperBound: false,
             terms: 
              [ Term {
                  variable: Variable { id: 'nachos', cost: 75, index: 1, value: 0, priority: 0 },
                  coefficient: 21 },
                Term {
                  variable: Variable { id: 'tacos', cost: 90, index: 2, value: 1, priority: 0 },
                  coefficient: 26 },
                Term {
                  variable: Variable { id: 'burritos', cost: 105, index: 3, value: 0, priority: 0 },
                  coefficient: 31 },
                Term {
                  variable: Variable { id: 'fajitas', cost: 120, index: 4, value: 0, priority: 0 },
                  coefficient: 36 } ],
             termsByVarIndex: 
              { '1': 
                 Term {
                   variable: Variable { id: 'nachos', cost: 75, index: 1, value: 0, priority: 0 },
                   coefficient: 21 },
                '2': 
                 Term {
                   variable: Variable { id: 'tacos', cost: 90, index: 2, value: 1, priority: 0 },
                   coefficient: 26 },
                '3': 
                 Term {
                   variable: Variable { id: 'burritos', cost: 105, index: 3, value: 0, priority: 0 },
                   coefficient: 31 },
                '4': 
                 Term {
                   variable: Variable { id: 'fajitas', cost: 120, index: 4, value: 0, priority: 0 },
                   coefficient: 36 } },
             relaxation: null } ],
        nConstraints: 1,
        nVariables: 4,
        isMinimization: true,
        availableIndexes: [],
        lastElementIndex: 5,
        tableauInitialized: true,
        relaxationIndex: 1 },
     matrix: 
      [ [ 89.99999999999999,
          -75.00000000000001,
          -90,
          -105,
          -120.00000000000001 ],
        [ 1.0000000000000004,
          -21.000000000000004,
          -26,
          -31.000000000000004,
          -36 ],
        [ 0, 0, 0, 0, 1 ],
        [ 0, 0, 0, 1, 0 ],
        [ 1, 0, -1, 0, 0 ],
        [ 2, -1, 0, 0, 0 ] ],
     width: 5,
     height: 5,
     costRowIndex: 0,
     rhsColumn: 0,
     variableIds: [ <1 empty item>, 'nachos', 'tacos', 'burritos', 'fajitas' ],
     unrestrictedVars: {},
     feasible: true,
     evaluation: 90,
     basicIndexes: [ -1, 0, 5, 6, 2 ],
     nonBasicIndexes: [ -1, 1, 7, 3, 4 ],
     rows: [ 1, -1, 4, -1, -1, 2, 3, -1, -1 ],
     cols: [ -1, 1, -1, 3, 4, -1, -1, 2, 1 ],
     precision: 1e-8,
     optionalObjectives: [],
     objectivesByPriority: {},
     savedState: 
      Tableau {
        model: 
         Model {
           tableau: [Circular],
           name: undefined,
           variables: 
            [ Variable { id: 'nachos', cost: 75, index: 1, value: 0, priority: 0 },
              Variable { id: 'tacos', cost: 90, index: 2, value: 1, priority: 0 },
              Variable { id: 'burritos', cost: 105, index: 3, value: 0, priority: 0 },
              Variable { id: 'fajitas', cost: 120, index: 4, value: 0, priority: 0 } ],
           variableIds: [ <1 empty item>, 'nachos', 'tacos', 'burritos', 'fajitas' ],
           integerVariables: 
            [ Variable { id: 'nachos', cost: 75, index: 1, value: 0, priority: 0 },
              Variable { id: 'tacos', cost: 90, index: 2, value: 1, priority: 0 },
              Variable { id: 'burritos', cost: 105, index: 3, value: 0, priority: 0 },
              Variable { id: 'fajitas', cost: 120, index: 4, value: 0, priority: 0 } ],
           unrestrictedVariables: {},
           constraints: 
            [ Constraint {
                index: 0,
                model: [Circular],
                rhs: 25,
                isUpperBound: false,
                terms: 
                 [ Term {
                     variable: Variable { id: 'nachos', cost: 75, index: 1, value: 0, priority: 0 },
                     coefficient: 21 },
                   Term {
                     variable: Variable { id: 'tacos', cost: 90, index: 2, value: 1, priority: 0 },
                     coefficient: 26 },
                   Term {
                     variable: Variable { id: 'burritos', cost: 105, index: 3, value: 0, priority: 0 },
                     coefficient: 31 },
                   Term {
                     variable: Variable { id: 'fajitas', cost: 120, index: 4, value: 0, priority: 0 },
                     coefficient: 36 } ],
                termsByVarIndex: 
                 { '1': 
                    Term {
                      variable: Variable { id: 'nachos', cost: 75, index: 1, value: 0, priority: 0 },
                      coefficient: 21 },
                   '2': 
                    Term {
                      variable: Variable { id: 'tacos', cost: 90, index: 2, value: 1, priority: 0 },
                      coefficient: 26 },
                   '3': 
                    Term {
                      variable: Variable { id: 'burritos', cost: 105, index: 3, value: 0, priority: 0 },
                      coefficient: 31 },
                   '4': 
                    Term {
                      variable: Variable { id: 'fajitas', cost: 120, index: 4, value: 0, priority: 0 },
                      coefficient: 36 } },
                relaxation: null } ],
           nConstraints: 1,
           nVariables: 4,
           isMinimization: true,
           availableIndexes: [],
           lastElementIndex: 5,
           tableauInitialized: true,
           relaxationIndex: 1 },
        matrix: 
         [ [ 83.33333333333333,
             -5,
             -3.3333333333333286,
             -1.6666666666666572,
             -3.3333333333333335 ],
           [ 0.6944444444444444,
             0.5833333333333334,
             0.7222222222222222,
             0.8611111111111112,
             -0.027777777777777776 ] ],
        width: 5,
        height: 2,
        costRowIndex: 0,
        rhsColumn: 0,
        variableIds: [ <1 empty item>, 'nachos', 'tacos', 'burritos', 'fajitas' ],
        unrestrictedVars: {},
        feasible: true,
        evaluation: 0,
        basicIndexes: [ -1, 4 ],
        nonBasicIndexes: [ -1, 1, 2, 3, 0 ],
        rows: [ -1, -1, -1, -1, 1 ],
        cols: [ 4, 1, 2, 3, -1 ],
        precision: 1e-8,
        optionalObjectives: [],
        objectivesByPriority: {},
        savedState: null,
        nVars: 5,
        integerIndexes: undefined,
        variables: 
         [ Variable { id: 'nachos', cost: 75, index: 1, value: 0, priority: 0 },
           Variable { id: 'tacos', cost: 90, index: 2, value: 1, priority: 0 },
           Variable { id: 'burritos', cost: 105, index: 3, value: 0, priority: 0 },
           Variable { id: 'fajitas', cost: 120, index: 4, value: 0, priority: 0 } ] },
     variables: 
      [ Variable { id: 'nachos', cost: 75, index: 1, value: 0, priority: 0 },
        Variable { id: 'tacos', cost: 90, index: 2, value: 1, priority: 0 },
        Variable { id: 'burritos', cost: 105, index: 3, value: 0, priority: 0 },
        Variable { id: 'fajitas', cost: 120, index: 4, value: 0, priority: 0 } ],
     nVars: 8,
     integerIndexes: undefined },
  iter: 9,
  solutionSet: { tacos: 1 } }

detail from new commit

MilpSolution {
  feasible: false,
  evaluation: 104.30860806,
  _tableau: 
   Tableau {
     model: 
      Model {
        tableau: [Circular],
        name: undefined,
        variables: 
         [ IntegerVariable { id: 'nachos', cost: 75, index: 1, value: 0, priority: 0 },
           IntegerVariable { id: 'tacos', cost: 90, index: 2, value: 0, priority: 0 },
           IntegerVariable { id: 'burritos', cost: 105, index: 3, value: 0, priority: 0 },
           IntegerVariable { id: 'fajitas', cost: 120, index: 4, value: 0, priority: 0 } ],
        integerVariables: 
         [ IntegerVariable { id: 'nachos', cost: 75, index: 1, value: 0, priority: 0 },
           IntegerVariable { id: 'tacos', cost: 90, index: 2, value: 0, priority: 0 },
           IntegerVariable { id: 'burritos', cost: 105, index: 3, value: 0, priority: 0 },
           IntegerVariable { id: 'fajitas', cost: 120, index: 4, value: 0, priority: 0 } ],
        unrestrictedVariables: {},
        constraints: 
         [ Constraint {
             slack: SlackVariable { id: 's0', cost: 0, index: 0, value: 0, priority: 0 },
             index: 0,
             model: [Circular],
             rhs: 25,
             isUpperBound: false,
             terms: 
              [ Term {
                  variable: IntegerVariable { id: 'nachos', cost: 75, index: 1, value: 0, priority: 0 },
                  coefficient: 21 },
                Term {
                  variable: IntegerVariable { id: 'tacos', cost: 90, index: 2, value: 0, priority: 0 },
                  coefficient: 26 },
                Term {
                  variable: IntegerVariable { id: 'burritos', cost: 105, index: 3, value: 0, priority: 0 },
                  coefficient: 31 },
                Term {
                  variable: IntegerVariable { id: 'fajitas', cost: 120, index: 4, value: 0, priority: 0 },
                  coefficient: 36 } ],
             termsByVarIndex: 
              { '1': 
                 Term {
                   variable: IntegerVariable { id: 'nachos', cost: 75, index: 1, value: 0, priority: 0 },
                   coefficient: 21 },
                '2': 
                 Term {
                   variable: IntegerVariable { id: 'tacos', cost: 90, index: 2, value: 0, priority: 0 },
                   coefficient: 26 },
                '3': 
                 Term {
                   variable: IntegerVariable { id: 'burritos', cost: 105, index: 3, value: 0, priority: 0 },
                   coefficient: 31 },
                '4': 
                 Term {
                   variable: IntegerVariable { id: 'fajitas', cost: 120, index: 4, value: 0, priority: 0 },
                   coefficient: 36 } },
             relaxation: null } ],
        nConstraints: 1,
        nVariables: 4,
        isMinimization: true,
        tableauInitialized: true,
        relaxationIndex: 1 },
     matrix: 
      [ [ 209.99999999999997,
          -29.99999999999997,
          -254.90384615384636,
          -18.505996691480544,
          -74.99999999999999 ],
        [ 35,
          -7.999999999999993,
          -76.47115384615391,
          -5.051799007444167,
          -20.999999999999993 ],
        [ 2, 0, 0, 0, -1 ],
        [ 2.6818181818181808,
          -1.1363636363636356,
          -4.827724358974361,
          -0.6345832706719798,
          -1.9090909090909083 ],
        [ 2.681818181818181,
          -0.7721445221445216,
          -4.827724358974362,
          -0.43093387347419576,
          -1.9090909090909085 ],
        [ 0.4999999999999998,
          0.5000000000000002,
          -2.1241987179487203,
          0.7207833609043288,
          0 ],
        [ -0.9999999999999999, 0, 0, 0, 1 ],
        [ 0.3087015851716619,
          0.3087015851716621,
          -2.3114870229007636,
          0.44501393215304885,
          0 ],
        [ -0.4999999999999998,
          -0.49999999999999933,
          -2.1241987179487185,
          -0.19368970916958508,
          -0 ],
        [ -0.4999999999999998,
          -0.49999999999999933,
          -2.1241987179487185,
          -0.27921663909567096,
          0 ],
        [ -2, -0, -0, -0, 1 ],
        [ -0.4999999999999998,
          -0.49999999999999933,
          -2.1241987179487185,
          -0.19368970916958508,
          -0 ],
        [ -0.4999999999999998,
          -0.49999999999999933,
          -2.1241987179487185,
          -0.27921663909567096,
          0 ],
        [ -1, -0, -0, -0, 1 ],
        [ 1, 0, 0, 0, -1 ],
        [ -0.4999999999999998,
          -0.49999999999999933,
          -2.1241987179487185,
          -0.19368970916958508,
          -0 ],
        [ -0.4999999999999998,
          -0.49999999999999933,
          -2.1241987179487185,
          -0.27921663909567096,
          0 ] ],
     width: 5,
     height: 17,
     costRowIndex: 0,
     rhsColumn: 0,
     variablesPerIndex: 
      [ SlackVariable { id: 's0', cost: 0, index: 0, value: 0, priority: 0 },
        IntegerVariable { id: 'nachos', cost: 75, index: 1, value: 0, priority: 0 },
        IntegerVariable { id: 'tacos', cost: 90, index: 2, value: 0, priority: 0 },
        IntegerVariable { id: 'burritos', cost: 105, index: 3, value: 0, priority: 0 },
        IntegerVariable { id: 'fajitas', cost: 120, index: 4, value: 0, priority: 0 },
        SlackVariable { id: 's5', cost: 0, index: 5, value: 0, priority: 0 },
        SlackVariable { id: 's6', cost: 0, index: 6, value: 0, priority: 0 },
        SlackVariable { id: 's7', cost: 0, index: 7, value: 0, priority: 0 },
        SlackVariable { id: 's8', cost: 0, index: 8, value: 0, priority: 0 },
        SlackVariable { id: 's9', cost: 0, index: 9, value: 0, priority: 0 },
        SlackVariable { id: 's10', cost: 0, index: 10, value: 0, priority: 0 },
        SlackVariable { id: 's11', cost: 0, index: 11, value: 0, priority: 0 },
        SlackVariable { id: 's12', cost: 0, index: 12, value: 0, priority: 0 },
        SlackVariable { id: 's13', cost: 0, index: 13, value: 0, priority: 0 },
        SlackVariable { id: 's14', cost: 0, index: 14, value: 0, priority: 0 },
        SlackVariable { id: 's15', cost: 0, index: 15, value: 0, priority: 0 },
        SlackVariable { id: 's16', cost: 0, index: 16, value: 0, priority: 0 },
        SlackVariable { id: 's17', cost: 0, index: 17, value: 0, priority: 0 },
        SlackVariable { id: 's18', cost: 0, index: 18, value: 0, priority: 0 },
        SlackVariable { id: 's19', cost: 0, index: 19, value: 0, priority: 0 } ],
     unrestrictedVars: {},
     feasible: false,
     evaluation: 104.30860806,
     varIndexByRow: [ -1, 0, 1, 6, 5, 4, 7, 8, 11, 12, 13, 14, 15, 16, 17, 18, 19 ],
     varIndexByCol: [ -1, 2, 9, 3, 10 ],
     rowByVarIndex: [ 1, 2, -1, -1, 5, 4, 3, 6, 7, -1, -1, 8, 9, 10, 11, 12, 13, 14, 15, 16 ],
     colByVarIndex: [ -1, -1, 1, 3, -1, -1, -1, -1, -1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1 ],
     precision: 1e-8,
     optionalObjectives: [],
     objectivesByPriority: {},
     savedState: null,
     availableIndexes: [],
     lastElementIndex: 20,
     variables: 
      [ IntegerVariable { id: 'nachos', cost: 75, index: 1, value: 0, priority: 0 },
        IntegerVariable { id: 'tacos', cost: 90, index: 2, value: 0, priority: 0 },
        IntegerVariable { id: 'burritos', cost: 105, index: 3, value: 0, priority: 0 },
        IntegerVariable { id: 'fajitas', cost: 120, index: 4, value: 0, priority: 0 } ],
     nVars: 22 },
  iter: 5,
  solutionSet: { nachos: 2, fajitas: 0.5 } }

Solving too long

Hello,
first I would like to thank Justin for this project.
The solver finds correct solution for many tasks (I have tested around 1000 tasks with 10-30 variables), many of them last less then second, but some of the tasks last very long (minutes, tens of minutes). Why? Is it possible to find the solution fast? I don't understand theory of LP. I give an example of the task that lasts too long. Thank you.

"{"constraints":{"c0":{"min":2.999},"c1":{"min":-3.001},"c2":{"min":3.999},"c3":{"min":-4.001},"c4":{"min":4.999},"c5":{"min":-5.001},"c6":{"min":2.999},"c7":{"min":-3.001},"c8":{"min":2.999},"c9":{"min":-3.001},"c10":{"min":3.999},"c11":{"min":-4.001},"c12":{"min":4.999},"c13":{"min":-5.001},"c14":{"min":3.999},"c15":{"min":-4.001},"c16":{"min":2.999},"c17":{"min":-3.001},"c18":{"min":2.999},"c19":{"min":-3.001},"c20":{"min":-22.001},"c21":{"min":-15.001},"c22":{"min":-0.001},"c23":{"min":-0.001},"c24":{"min":-0.001},"c25":{"min":-0.001},"c26":{"min":-0.001},"c27":{"min":-0.001},"c28":{"min":-0.001},"c29":{"min":-0.001},"c30":{"min":-0.001},"c31":{"min":-0.001},"c32":{"min":-0.001},"c33":{"min":-0.001},"c34":{"min":-0.001},"c35":{"min":-0.001},"c36":{"min":-0.001},"c37":{"min":-0.001},"c38":{"min":-0.001},"c39":{"min":-0.001},"c40":{"min":-0.001}},"variables":{"x0":{"objective":10,"c0":1,"c1":-1,"c2":0,"c3":0,"c4":0,"c5":0,"c6":0,"c7":0,"c8":0,"c9":0,"c10":0,"c11":0,"c12":0,"c13":0,"c14":0,"c15":0,"c16":0,"c17":0,"c18":0,"c19":0,"c20":-1,"c21":0,"c22":1,"c23":0,"c24":0,"c25":0,"c26":0,"c27":0,"c28":0,"c29":0,"c30":0,"c31":0,"c32":0,"c33":0,"c34":0,"c35":0,"c36":0,"c37":0,"c38":0,"c39":0,"c40":0},"x1":{"objective":10,"c0":0,"c1":0,"c2":1,"c3":-1,"c4":0,"c5":0,"c6":0,"c7":0,"c8":0,"c9":0,"c10":0,"c11":0,"c12":0,"c13":0,"c14":0,"c15":0,"c16":0,"c17":0,"c18":0,"c19":0,"c20":-1,"c21":0,"c22":0,"c23":1,"c24":0,"c25":0,"c26":0,"c27":0,"c28":0,"c29":0,"c30":0,"c31":0,"c32":0,"c33":0,"c34":0,"c35":0,"c36":0,"c37":0,"c38":0,"c39":0,"c40":0},"x2":{"objective":10,"c0":0,"c1":0,"c2":1,"c3":-1,"c4":0,"c5":0,"c6":0,"c7":0,"c8":0,"c9":0,"c10":0,"c11":0,"c12":0,"c13":0,"c14":0,"c15":0,"c16":0,"c17":0,"c18":0,"c19":0,"c20":0,"c21":-1,"c22":0,"c23":0,"c24":1,"c25":0,"c26":0,"c27":0,"c28":0,"c29":0,"c30":0,"c31":0,"c32":0,"c33":0,"c34":0,"c35":0,"c36":0,"c37":0,"c38":0,"c39":0,"c40":0},"x3":{"objective":10,"c0":0,"c1":0,"c2":0,"c3":0,"c4":1,"c5":-1,"c6":0,"c7":0,"c8":0,"c9":0,"c10":0,"c11":0,"c12":0,"c13":0,"c14":0,"c15":0,"c16":0,"c17":0,"c18":0,"c19":0,"c20":-1,"c21":0,"c22":0,"c23":0,"c24":0,"c25":1,"c26":0,"c27":0,"c28":0,"c29":0,"c30":0,"c31":0,"c32":0,"c33":0,"c34":0,"c35":0,"c36":0,"c37":0,"c38":0,"c39":0,"c40":0},"x4":{"objective":10,"c0":0,"c1":0,"c2":0,"c3":0,"c4":1,"c5":-1,"c6":0,"c7":0,"c8":0,"c9":0,"c10":0,"c11":0,"c12":0,"c13":0,"c14":0,"c15":0,"c16":0,"c17":0,"c18":0,"c19":0,"c20":0,"c21":-1,"c22":0,"c23":0,"c24":0,"c25":0,"c26":1,"c27":0,"c28":0,"c29":0,"c30":0,"c31":0,"c32":0,"c33":0,"c34":0,"c35":0,"c36":0,"c37":0,"c38":0,"c39":0,"c40":0},"x5":{"objective":10,"c0":0,"c1":0,"c2":0,"c3":0,"c4":0,"c5":0,"c6":1,"c7":-1,"c8":0,"c9":0,"c10":0,"c11":0,"c12":0,"c13":0,"c14":0,"c15":0,"c16":0,"c17":0,"c18":0,"c19":0,"c20":-1,"c21":0,"c22":0,"c23":0,"c24":0,"c25":0,"c26":0,"c27":1,"c28":0,"c29":0,"c30":0,"c31":0,"c32":0,"c33":0,"c34":0,"c35":0,"c36":0,"c37":0,"c38":0,"c39":0,"c40":0},"x6":{"objective":10,"c0":0,"c1":0,"c2":0,"c3":0,"c4":0,"c5":0,"c6":1,"c7":-1,"c8":0,"c9":0,"c10":0,"c11":0,"c12":0,"c13":0,"c14":0,"c15":0,"c16":0,"c17":0,"c18":0,"c19":0,"c20":0,"c21":-1,"c22":0,"c23":0,"c24":0,"c25":0,"c26":0,"c27":0,"c28":1,"c29":0,"c30":0,"c31":0,"c32":0,"c33":0,"c34":0,"c35":0,"c36":0,"c37":0,"c38":0,"c39":0,"c40":0},"x7":{"objective":10,"c0":0,"c1":0,"c2":0,"c3":0,"c4":0,"c5":0,"c6":0,"c7":0,"c8":1,"c9":-1,"c10":0,"c11":0,"c12":0,"c13":0,"c14":0,"c15":0,"c16":0,"c17":0,"c18":0,"c19":0,"c20":-1,"c21":0,"c22":0,"c23":0,"c24":0,"c25":0,"c26":0,"c27":0,"c28":0,"c29":1,"c30":0,"c31":0,"c32":0,"c33":0,"c34":0,"c35":0,"c36":0,"c37":0,"c38":0,"c39":0,"c40":0},"x8":{"objective":10,"c0":0,"c1":0,"c2":0,"c3":0,"c4":0,"c5":0,"c6":0,"c7":0,"c8":1,"c9":-1,"c10":0,"c11":0,"c12":0,"c13":0,"c14":0,"c15":0,"c16":0,"c17":0,"c18":0,"c19":0,"c20":0,"c21":-1,"c22":0,"c23":0,"c24":0,"c25":0,"c26":0,"c27":0,"c28":0,"c29":0,"c30":1,"c31":0,"c32":0,"c33":0,"c34":0,"c35":0,"c36":0,"c37":0,"c38":0,"c39":0,"c40":0},"x9":{"objective":10,"c0":0,"c1":0,"c2":0,"c3":0,"c4":0,"c5":0,"c6":0,"c7":0,"c8":0,"c9":0,"c10":1,"c11":-1,"c12":0,"c13":0,"c14":0,"c15":0,"c16":0,"c17":0,"c18":0,"c19":0,"c20":-1,"c21":0,"c22":0,"c23":0,"c24":0,"c25":0,"c26":0,"c27":0,"c28":0,"c29":0,"c30":0,"c31":1,"c32":0,"c33":0,"c34":0,"c35":0,"c36":0,"c37":0,"c38":0,"c39":0,"c40":0},"x10":{"objective":10,"c0":0,"c1":0,"c2":0,"c3":0,"c4":0,"c5":0,"c6":0,"c7":0,"c8":0,"c9":0,"c10":1,"c11":-1,"c12":0,"c13":0,"c14":0,"c15":0,"c16":0,"c17":0,"c18":0,"c19":0,"c20":0,"c21":-1,"c22":0,"c23":0,"c24":0,"c25":0,"c26":0,"c27":0,"c28":0,"c29":0,"c30":0,"c31":0,"c32":1,"c33":0,"c34":0,"c35":0,"c36":0,"c37":0,"c38":0,"c39":0,"c40":0},"x11":{"objective":10,"c0":0,"c1":0,"c2":0,"c3":0,"c4":0,"c5":0,"c6":0,"c7":0,"c8":0,"c9":0,"c10":0,"c11":0,"c12":1,"c13":-1,"c14":0,"c15":0,"c16":0,"c17":0,"c18":0,"c19":0,"c20":-1,"c21":0,"c22":0,"c23":0,"c24":0,"c25":0,"c26":0,"c27":0,"c28":0,"c29":0,"c30":0,"c31":0,"c32":0,"c33":1,"c34":0,"c35":0,"c36":0,"c37":0,"c38":0,"c39":0,"c40":0},"x12":{"objective":10,"c0":0,"c1":0,"c2":0,"c3":0,"c4":0,"c5":0,"c6":0,"c7":0,"c8":0,"c9":0,"c10":0,"c11":0,"c12":1,"c13":-1,"c14":0,"c15":0,"c16":0,"c17":0,"c18":0,"c19":0,"c20":0,"c21":-1,"c22":0,"c23":0,"c24":0,"c25":0,"c26":0,"c27":0,"c28":0,"c29":0,"c30":0,"c31":0,"c32":0,"c33":0,"c34":1,"c35":0,"c36":0,"c37":0,"c38":0,"c39":0,"c40":0},"x13":{"objective":10,"c0":0,"c1":0,"c2":0,"c3":0,"c4":0,"c5":0,"c6":0,"c7":0,"c8":0,"c9":0,"c10":0,"c11":0,"c12":0,"c13":0,"c14":1,"c15":-1,"c16":0,"c17":0,"c18":0,"c19":0,"c20":-1,"c21":0,"c22":0,"c23":0,"c24":0,"c25":0,"c26":0,"c27":0,"c28":0,"c29":0,"c30":0,"c31":0,"c32":0,"c33":0,"c34":0,"c35":1,"c36":0,"c37":0,"c38":0,"c39":0,"c40":0},"x14":{"objective":10,"c0":0,"c1":0,"c2":0,"c3":0,"c4":0,"c5":0,"c6":0,"c7":0,"c8":0,"c9":0,"c10":0,"c11":0,"c12":0,"c13":0,"c14":1,"c15":-1,"c16":0,"c17":0,"c18":0,"c19":0,"c20":0,"c21":-1,"c22":0,"c23":0,"c24":0,"c25":0,"c26":0,"c27":0,"c28":0,"c29":0,"c30":0,"c31":0,"c32":0,"c33":0,"c34":0,"c35":0,"c36":1,"c37":0,"c38":0,"c39":0,"c40":0},"x15":{"objective":10,"c0":0,"c1":0,"c2":0,"c3":0,"c4":0,"c5":0,"c6":0,"c7":0,"c8":0,"c9":0,"c10":0,"c11":0,"c12":0,"c13":0,"c14":0,"c15":0,"c16":1,"c17":-1,"c18":0,"c19":0,"c20":-1,"c21":0,"c22":0,"c23":0,"c24":0,"c25":0,"c26":0,"c27":0,"c28":0,"c29":0,"c30":0,"c31":0,"c32":0,"c33":0,"c34":0,"c35":0,"c36":0,"c37":1,"c38":0,"c39":0,"c40":0},"x16":{"objective":10,"c0":0,"c1":0,"c2":0,"c3":0,"c4":0,"c5":0,"c6":0,"c7":0,"c8":0,"c9":0,"c10":0,"c11":0,"c12":0,"c13":0,"c14":0,"c15":0,"c16":1,"c17":-1,"c18":0,"c19":0,"c20":0,"c21":-1,"c22":0,"c23":0,"c24":0,"c25":0,"c26":0,"c27":0,"c28":0,"c29":0,"c30":0,"c31":0,"c32":0,"c33":0,"c34":0,"c35":0,"c36":0,"c37":0,"c38":1,"c39":0,"c40":0},"x17":{"objective":10,"c0":0,"c1":0,"c2":0,"c3":0,"c4":0,"c5":0,"c6":0,"c7":0,"c8":0,"c9":0,"c10":0,"c11":0,"c12":0,"c13":0,"c14":0,"c15":0,"c16":0,"c17":0,"c18":1,"c19":-1,"c20":-1,"c21":0,"c22":0,"c23":0,"c24":0,"c25":0,"c26":0,"c27":0,"c28":0,"c29":0,"c30":0,"c31":0,"c32":0,"c33":0,"c34":0,"c35":0,"c36":0,"c37":0,"c38":0,"c39":1,"c40":0},"x18":{"objective":10,"c0":0,"c1":0,"c2":0,"c3":0,"c4":0,"c5":0,"c6":0,"c7":0,"c8":0,"c9":0,"c10":0,"c11":0,"c12":0,"c13":0,"c14":0,"c15":0,"c16":0,"c17":0,"c18":1,"c19":-1,"c20":0,"c21":-1,"c22":0,"c23":0,"c24":0,"c25":0,"c26":0,"c27":0,"c28":0,"c29":0,"c30":0,"c31":0,"c32":0,"c33":0,"c34":0,"c35":0,"c36":0,"c37":0,"c38":0,"c39":0,"c40":1}},"ints":{"x0":1,"x1":1,"x2":1,"x3":1,"x4":1,"x5":1,"x6":1,"x7":1,"x8":1,"x9":1,"x10":1,"x11":1,"x12":1,"x13":1,"x14":1,"x15":1,"x16":1,"x17":1,"x18":1},"optimize":"objective","opType":"min"}"

Strange behaviour

Hi,

I've found your solver, and it's the most clear that I found for my porpouses, but I've detected some strange behaviour and after looking the code for hours I haven't been able to understand where's the problem is...

I need to get a combination of medicaments, given a minimum dose, for the lowest price.
My arrays are as follow:

var arrV = {
"drug0":
{"price":"38.11","dosis":75},
"drug1":
{"price":"299.83","dosis":750},
"drug2":
{"price":"167.58","dosis":300},
"drug3":
{"price":"227.49","dosis":450},
"drug4":
{"price":"412.43","dosis":900},
"drug5":
{"price":"454.70","dosis":1050}};

var arrI = {"drug0":1,"drug1":1,"drug2":1,"drug3":1,"drug4":1,"drug5":1};

The I call solver like this:

solver = new Solver();
model = {
"optimize": "price",
"opType": "min",
"constraints": {
"dosis": {"min": minDosis},
},
"variables": arrV,
"ints": arrI
};
results = solver.Solve(model);

If for example "minDosis" is 750, I expect to get 1 drug1, and if is 375, 1 drug0 and 1 drug2.
But, let's supose I want a mininum dose of 200. We expect 3 drug0 (114,33 total price), but instead I get 1 drug1 (total price 299,83). If instead of 200 I put a minimum dose of 199 or 201, it works as expected.
I've tried everything, but I cannot find where the bug is...

Multi-Objective Optimization

@bchevalier

What are your thoughts on trying to handle optimization problems with multiple objectives?
I've figured out a way to do it, but it's not pretty; and I wanted your input on the route I'm taking, and if its even worth including.

Here's a use case:

Some diets allow you to eat as much junk food as you want and still lose weight and gain muscle
as long as your ratio of carbs to protein to fat stays in check.

My optimal ratio requires me to eat 375 grams of carbs, 225 grams of protein, and 66.666 grams
of fat per day. I also want to eat as much bacon, cheese, and french fries as possible.

How much of what should I eat to satisfy these requirements?

Below is what I'm proposing multi-opt models look like (no code changes need to happen with what you've done, since models with an array for the optimize attribute could be handled differently).

{ 
    "name": "Food Problem",
    "optimize": [
        {"key":"bacon", "opType": "max"},
        {"key": "cheddar cheese", "opType": "max"},
        {"key": "french fries", "opType": "max"}
    ],
    "constraints": { 
        "carb": { "equal": 375 },
        "protein": { "equal": 225 },
        "fat": { "equal": 66.666 } 
    },
    "variables": { 
         "egg white":{ "carb": 0.0073, "protein": 0.109, "fat": 0.0017, "egg white": 1 },
         "egg whole":{ "carb": 0.0072, "protein": 0.1256, "fat": 0.0951, "egg whole": 1 },
         "cheddar cheese":{ "carb": 0.0128, "protein": 0.249, "fat": 0.3314, "cheddar cheese": 1 },
         "bacon":{ "carb": 0.00667, "protein": 0.116, "fat": 0.4504, "bacon": 1 },
         "potato": { "carb": 0.1747, "protein": 0.0202, "fat": 0.0009, "potato": 1 },
         "french fries": { "carb": 0.3902, "protein": 0.038, "fat": 0.1612, "french fries": 1 }
    } 
}

Here's what I've come up with so far, referencing the above model:

  1. Maximize and minimize the model for each attribute to be optimized
bacon: {most: 138, least: 0}
cheese: {most: 189, least: 0}
fries: {most: 388, least: 0}
  1. Add a nonsense attribute to each variable that you'll use to solve on, since this solution works by updating constraints and making sure the model is feasible
  2. Create a function that accepts an array which represents what the min for each optimization attribute should be (or max if the attribute is being minimized).

For example, I could give it [44, 66, 115](for bacon, cheese, and fries), and it would update the model to look like this:

    "constraints": { 
        "carb": { "equal": 375 },
        "protein": { "equal": 225 },
        "fat": { "equal": 66.666 },
        "bacon": { "min": 44},
        "cheddar cheese": { "min": 66 },
        "french fries": { "min": 115 }        
    }

3a. The function would then solve the model with these new constraints, returning the following:

{ feasible: true,
  result: 3534.71621236,
  bacon: 49.05518453,
  'egg white': 1483.68745307,
  potato: 1820.97357477,
  'cheddar cheese': 66,
  'french fries': 115}

3b. Compare the results for the optimization attributes to their potential best (step 1). For instance, for cheese in this example, you'd go:

   (66 - 0 (worst)) / (189(best) - 0(worst))

Which returns 0.35. Multiply all of the results together to get 0.0366677. This is our score. Return it out.

  1. Create some optimization function (gradient descent?) that will maximize the score in as few steps as possible.

Below is what I have so far (since I can't upload it) where I use brute force to solve the above problem. Very un-refined at this point and very much a work in progress.

var solver = require("./src/solver");


var the_model = { 
    "name": "Food Problem",
    "optimize": [
        {"key":"bacon", "opType": "max"},
        {"key": "cheddar cheese", "opType": "max"},
        {"key": "french fries", "opType": "max"}
    ],
    "constraints": { 
        "carb": { "equal": 375 },
        "protein": { "equal": 225 },
        "fat": { "equal": 66.666 } 
    },
    "variables": { 
         "egg white":{ "carb": 0.0073, "protein": 0.109, "fat": 0.0017, "egg white": 1 },
         "egg whole":{ "carb": 0.0072, "protein": 0.1256, "fat": 0.0951, "egg whole": 1 },
         "cheddar cheese":{ "carb": 0.0128, "protein": 0.249, "fat": 0.3314, "cheddar cheese": 1 },
         "bacon":{ "carb": 0.00667, "protein": 0.116, "fat": 0.4504, "bacon": 1 },
         "potato": { "carb": 0.1747, "protein": 0.0202, "fat": 0.0009, "potato": 1 },
         "french fries": { "carb": 0.3902, "protein": 0.038, "fat": 0.1612, "french fries": 1 }
    } 
};

function Fit_Fx(){};

Fit_Fx.prototype.load = function(model){

    // Some variables to help
    // keep track of stuff down
    // below
    var tmp_obj = {},
        tmp_sol = {},
        tmp_key = "",
        tmp_num = 0;


    // Create an object on *this* to house
    // all of our fitness info
    this._data = {

        // Where we hold the things
        // we want to optimize
        "targets": {},

        // This'll be easier than
        // having to eat up `Object.keys`
        "keys": [],

        // And we'll want a hard and fast
        // version of the model
        // so here
        "model": JSON.parse(JSON.stringify(model))
    };

    // Loop over the array of objectives
    // storing the pertinents of each
    // somewhere in our _data object
    for(var i = 0; i < model.optimize.length; i++){

        // get the key we're working with
        tmp_key = model.optimize[i].key;

        // Load the target's key into the "Keys" object
        this._data.keys.push(tmp_key)

        // push the model's details into this._data.targets
        tmp_obj = this._data.targets[model.optimize[i].key] = model.optimize[i];

        // add a best and worst to each of these objects
        // not sure if we'll need them; but in case we do,
        // it'll be nice #YAGNI
        tmp_obj.best = 0;
        tmp_obj.worst = tmp_obj.opType === "max" ? 1e99 : 0;

    }

    // Loop over our keys, and figure out what the optimum
    // is for each target in isolation...
    for(i = 0; i < this._data.keys.length; i++){

        // since we're not doing anything *too* weird
        // we don't need to `JSON.parse(stringify))`
        // the model
        tmp_obj = this._data.model;

        // set the model's optimize and optypes for best
        tmp_obj.optimize = this._data.keys[i];
        tmp_obj.opType = this._data.targets[tmp_obj.optimize].opType;

        // Solve the model
        tmp_sol = solver.Solve(tmp_obj);

        // Update the best
        this._data.targets[tmp_obj.optimize].best = tmp_sol.result;

        // Do the whole thing again, but aimed @ worst
        tmp_obj.opType = tmp_obj.opType === "max" ? "min" : "max";

        // Solve the bad model
        tmp_sol = solver.Solve(tmp_obj);

        // Note to self...
        // this feels like shadow prices
        // or something...
        // Because this is aimed at "trivial"
        // examples, this isn't expensive; 
        // but there's probably
        // a lot more effecient way of
        // doing this...

        // Update the worst
        this._data.targets[tmp_obj.optimize].worst = tmp_sol.result;
    }

    // Finally, give each variable some non-sense
    // attribute to optimize
    for(i in this._data.model.variables){
        this._data.model.variables[i].fake_target = 1;
    }


}

// Easy function to get details out
// should we want them...
Fit_Fx.prototype.details = function(){
    return this._data;
}

// Now we do the actual scoring function
Fit_Fx.prototype.score = function(vector){

    // Copy the model, get the keys, and targets
    var model = this._data.model,
        keys = this._data.keys,
        targets = this._data.targets,
        tmp_opt = "",
        tmp_sol = {},
        tmp_obj = {},
        tmp_num = 0,
        total = 1;

    // Update the model to go after the fake target
    // and set optype to "max" because...who cares
    model.opType = "max";
    model.optimize = "fake_target";

    // Set Constraints based on the vectors
    for(var i = 0; i < keys.length; i++){
        model.constraints[keys[i]] = model.constraints[keys[i]] || {};
        tmp_opt = targets[keys[i]].opType = "max" ? "min" : "max";
        model.constraints[keys[i]][tmp_opt] = vector[i];
    }

    // Solve it now!
    tmp_sol = solver.Solve(model);

    // Make sure its feasible
    if(tmp_sol.feasible){

        // Iterate over the keys again...
        for(i = 0; i < keys.length; i++){

            tmp_obj = targets[keys[i]];

            // First off, does the key
            // exist in the solution?
            if(tmp_sol[keys[i]]){
                if(tmp_obj.opType = "max"){
                    tmp_num = (tmp_sol[keys[i]] - tmp_obj.worst) / (tmp_obj.best - tmp_obj.worst);
                    total *= tmp_num;
                } else {

                }
            } else {

                // target doesn't exist in the solution
                // set Return a score of 0
                return 0
            }
        }

        // Return what we got
        tmp_sol.score = total;
        return tmp_sol;
    } else {

        // Solution isn't feasible...
        return 0;
    }

}

function loop_index(nbr, arry){
    var rtrn = [];

    for(var i = 0; i < arry.length; i++){
        rtrn.push(nbr % arry[i]);
        nbr = Math.floor(nbr / arry[i]);
    }
    return rtrn;
}

// Instantiate the function
var fitness = new Fit_Fx();

// Load the model into the function
// to set up scoring
fitness.load(the_model);

// Play Example for testing

var ary = [138, 189, 388],
        tst = [],
        tmp,
        rslts = [],
        global = {score: -1e99};

var num = 11;

for(i = 0; i < num * num * num; i++){
    tst = (loop_index(i,[num, num, num]).map(function(d, b){return d/(num - 1) * ary[b]}));
    tmp = fitness.score(tst);

    if(tmp !== 0){
        global = tmp.score > global.score ? tmp : global;
    }
}


console.log(global);

Return all or "top x" optimizations

This is a re-ask from #46

@bchevalier mentioned that it wouldn't be too hard to add functionality for this, and it's definitely something I'd be interested in.

I would like to be able to return either all, or at least a specified amount, of the top feasible solutions to a problem.

I'm building a lineup generator where additional filtering/comparison needs to be done, so being able to get a few additional feasible solutions would be very helpful.

Thanks!

Limit Computation Time

I've got an IP that takes to long to solve. As I modeled it with gurobi previously I know that it reaches a feasible solutions after a short time and spents most of the time without improving the solution.
Is it possible to stop the computation early, e.g. after 20 seconds?

Clarify license?

Hi there,

I really like this library! Would you mind adding an explicit LICENSE file (package.json says MIT), and clarifying if there are external non-MIT-licensed dependencies? I'm guessing all the Tableau references are to Tableau-compatible data formats and expressions, but if it is not under the MIT license, I think that should be clarified.

Thank you!

Cutting Stock and Unexpected Result

The following model generates an unexpected result:

{
  "optimize": "cost",
  "opType": "min",
  "constraints": {
    "length": {
      "min": 25
    }
  },
  "variables": {
    "21": {
      "21": 1,
      "length": 21,
      "cost": 75
    },
    "26": {
      "26": 1,
      "length": 26,
      "cost": 90
    },
    "31": {
      "31": 1,
      "length": 31,
      "cost": 105
    },
    "36": {
      "36": 1,
      "length": 36,
      "cost": 120
    }
  },
  "ints": {
    "21": 1,
    "26": 1,
    "31": 1,
    "36": 1
  }
}

The result I get is:

{  
   "26":-0,
   "36":1,
   "feasible":true,
   "result":120,
   "bounded":true
}

What I'm trying to do is find the minimum number of panel lengths required to cover at least the minimum overall required length (the length of a building) while minimizing final cost of the panels. I believe this is a variation of of the cutting stock problem. In this case, the minimum required length is 25. I would expect the solver to return a result of "26": 1 rather than "36": 1. The cost in that case would be 90 rather than 120.

I'm assuming I constructed the model incorrectly somehow. Is it possible to achieve my intended result with a modification to the model?

Operators in Constraints

I would like to know if it was possible to include the option to add up properties of variables in the constraints section. Let's see an example: I have a scenario where a farmer wants to optimise his profits by evaluating the most profitable growing option:
{ "name": "Crop Rotation Problem", "optimize": "Gross Margin", "opType": "max", "constraints": { "field0": { "max": 1 }, "field1": { "max": 1 }, "Wheat": { "max": 10 } }, "variables": { "field00": { "field0": 1, "Wheat": 3.6, "Gross Margin": 3052.44 }, "field01": { "field0": 1, "Summer Wheat": 3.6, "Gross Margin": 1787.58 }, "field02": { "field0": 1, "Summer Barley": 3.6, "Gross Margin": 1789.65 }, "field10": { "field1": 1, "Corn": 11.9, "Gross Margin": 7243.830000000001 }, "field11": { "field1": 1, "Soybeans": 11.9, "Gross Margin": 4710.235 }, "field12": { "field1": 1, "Onions": 11.9, "Gross Margin": 46602.325000000004 }, }, "ints": { "field00": 1, "field01": 1, "field02": 1, "field10": 1, "field11": 1, "field12": 1, } }

Now for example imagine there was a policy restriction only allowing the farmer to grow a maximum of 75% of his entire acerage with the sum of Wheat and Summer Barley. To solve this, it would be nice if the Solver would be able to handle the following:

{ "name": "Crop Rotation Problem", "optimize": "Gross Margin", "opType": "max", "constraints": { "field0": { "max": 1 }, "field1": { "max": 1 }, "Wheat" + "Summer Barley": { "max": 10 } },...

Maybe there is a nicer and mathematically more appealing way to achieve this, but for now that's the only way I could think of.
Thanks already for the awesome work!

Min Max in variables

Hey ! Thank you so much for making this awesome library :)

I have a quick question, is it possible to set min,max conditions in your variables? (see example code below)

Example:
You have 4 types of products to ship (solo, combo, addon, ok) and you have different types of shipments (c1-1,c2-3, etc..) that fit different combinations of products and have different costs and time for delivery.
You need to ship ALL products but you can ship them in different ways. Some products can be shipped with others, some not and some products can ONLY be shipped with other products (addon).

We need to solve for integer values for the best combination of boxes to use while minimizing cost .

Example code below:

{ optimize: 'price',
  opType: 'min',
  constraints: 
   { solo: { min: 2, max: 2 },
     combo: { min: 2, max: 2 },
     addon: { min: 20, max: 20 },
     ok: { min: 3, max: 3 } },
  variables: 
   { 'c1-1': { solo: 1, price: 100, time: 3 },
     'c2-3': { addon: { min: 0, max:20}, price: 10, time: 2 },
     'c3-4': { combo: 1, addon: { min: 0, max:20 }, ok: 1, price: 20, time: 3 },
     'c3-5': { combo: 1, ok: 1, price: 20, time: 3 },
     'c3-6': { ok: 1, price: 20, time: 3 },
     'c4-7': { solo: 1, addon: { min: 0, max:5}, price: 15, time: 3 },
  ints: 
   { 'c1-1': 1,
     'c2-3': 1,
     'c3-4': 1,
     'c3-5': 1,
     'c3-6': 1,
     'c4-7': 1 
}
 }

is it possible to represent the fact that 'addon' products can be easily moved between any box that has capacity for them by adding :

addon: { min: 0, max:20 }

in a variable constraint ? If not is there a way to simulate it ?

Thanks again !

Job Shop Scheduling Question

I have been reading through your test problems and am trying to find one that could be used as an example to build a job shop scheduling optimization using your library. Is there one that could be mimicked for 450 variables and approx 10 constraints? If not, could this type of problem be solved with your library? I need to build a solution in JavaScript and your library seems to be the best shot.

Unexpected result

Thank you for implementing and supporting such a great tool!

I was playing with the an example and modified it to the following:

var solver = require('./src/solver'),
  results,
  model = {
    optimize: 'b',
    opType: 'min',
    constraints: {
      a: { min: 350 },
      b: { min: 1000 },
      c: { min: 60, max: 80 },
      d: { min: 0.8, max : 10 },
      e: { min: 12 },
      f: { min: 215},
      g: { min: 28 },
      h: { min: 10 },
      i: { min: 18 },
      j: { min: 65, max: 2500 },
    },
    variables: {
      var1:  { a: 1, b: 44.7, c: 1411, d: 2,    e: 365, f: 0,     g: 55.4, h: 33.3, i: 441, j: 0 },
      var2:  { a: 1, b: 11.6, c: 418,  d: 0.7,  e: 54,  f: 0,     g: 3.2,  h: 1.9,  i: 68,  j: 0 },
      var3:  { a: 1, b: 11.8, c: 377,  d: 14.4, e: 175, f: 0,     g: 14.4, h: 8.8,  i: 114, j: 0 },
      var4:  { a: 1, b: 11.4, c: 252,  d: 0.1,  e: 56,  f: 0,     g: 13.5, h: 2.3,  i: 68,  j: 0 },
      var5:  { a: 1, b: 36.0, c: 897,  d: 1.7,  e: 99,  f: 30.9,  g: 17.4, h: 7.9,  i: 106, j: 0 },
      var6:  { a: 1, b: 28.6, c: 680,  d: 0.8,  e: 80,  f: 0,     g: 10.6, h: 1.6,  i: 110, j: 0 },
      var7:  { a: 1, b: 21.2, c: 460,  d: 0.6,  e: 41,  f: 0,     g: 2,    h: 4.8,  i: 60,  j: 0 },
      var8:  { a: 1, b: 25.3, c: 907,  d: 5.1,  e: 341, f: 0,     g: 37.1, h: 8.9,  i: 64,  j: 0 },
      var9:  { a: 1, b: 15.0, c: 488,  d: 2.5,  e: 115, f: 0,     g: 13.8, h: 8.5,  i: 126, j: 0 },
      var10: { a: 1, b: 12.2, c: 484,  d: 2.7,  e: 125, f: 0,     g: 13.9, h: 6.4,  i: 160, j: 0 },
      var11: { a: 1, b: 12.4, c: 439,  d: 1.1,  e: 82,  f: 0,     g: 9.9,  h: 3,    i: 66,  j: 0 },
      var12: { a: 1, b: 8.0,  c: 130,  d: 0.4,  e: 31,  f: 18.9,  g: 2.8,  h: 3,    i: 17,  j: 0 },
      var13: { a: 1, b: 12.5, c: 288,  d: 0.5,  e: 50,  f: 0,     g: 0,    h: 0,    i: 0,   j: 0 },
      var14: { a: 1, b: 6.1,  c: 310,  d: 10.5, e: 18,  f: 16.8,  g: 4,    h: 16,   i: 7,   j: 177 },
      var15: { a: 1, b: 8.4,  c: 422,  d: 15.1, e: 9,   f: 26,    g: 3,    h: 23.5, i: 11,  j: 60 },
      var16: { a: 1, b: 10.8, c: 9,    d: 0.2,  e: 3,   f: 44.2,  g: 0,    h: 0.2,  i: 2,   j: 0 },
      var17: { a: 1, b: 20.6, c: 17,   d: 0.6,  e: 6,   f: 55.8,  g: 0.2,  h: 0,    i: 0,   j: 0 },
      var18: { a: 1, b: 2.9,  c: 238,  d: 1.0,  e: 52,  f: 18.6,  g: 2.8,  h: 6.5,  i: 1,   j: 0 },
      var19: { a: 1, b: 7.4,  c: 448,  d: 16.4, e: 19,  f: 28.1,  g: 0.8,  h: 10.3, i: 4,   j: 0 },
      var20: { a: 1, b: 3.5,  c: 49,   d: 1.7,  e: 3,   f: 16.9,  g: 0.6,  h: 2.5,  i: 0,   j: 17 },
      var21: { a: 1, b: 15.7, c: 661,  d: 1.0,  e: 48,  f: 0,     g: 9.6,  h: 8.1,  i: 471, j: 0 },
      var22: { a: 1, b: 8.6,  c: 18,   d: 0.2,  e: 8,   f: 2.7,   g: 0.4,  h: 0.5,  i: 0,   j: 0 },
      var23: { a: 1, b: 20.1, c: 0,    d: 0,    e: 0,   f: 0,     g: 0,    h: 0,    i: 0,   j: 0 },
      var24: { a: 1, b: 41.7, c: 0,    d: 0,    e: 0,   f: 0.2,   g: 0,    h: 0.5,  i: 5,   j: 0 },
      var25: { a: 1, b: 2.9,  c: 166,  d: 0.1,  e: 34,  f: 0.2,   g: 2.1,  h: 2.9,  i: 69,  j: 0 },
      var26: { a: 1, b: 2.2,  c: 214,  d: 0.1,  e: 32,  f: 0.4,   g: 2.5,  h: 2.4,  i: 87,  j: 0 },
      var27: { a: 1, b: 3.4,  c: 213,  d: 0.1,  e: 33,  f: 0,     g: 0,    h: 2,    i: 0,   j: 0 },
      var28: { a: 1, b: 3.6,  c: 309,  d: 0.2,  e: 46,  f: 0.4,   g: 1,    h: 4,    i: 120, j: 0 },
      var29: { a: 1, b: 8.5,  c: 404,  d: 0.2,  e: 62,  f: 0,     g: 0.9,  h: 0,    i: 0,   j: 0 },
      var30: { a: 1, b: 2.2,  c: 333,  d: 0.2,  e: 139, f: 169.2, g: 6.4,  h: 50.8, i: 316, j: 525 },
      var31: { a: 1, b: 3.1,  c: 245,  d: 0.1,  e: 20,  f: 0,     g: 2.8,  h: 3.9,  i: 86,  j: 0 },
      var32: { a: 1, b: 3.3,  c: 140,  d: 0.1,  e: 15,  f: 0,     g: 1.7,  h: 2.7,  i: 54,  j: 0 },
      var33: { a: 1, b: 3.5,  c: 196,  d: 0.2,  e: 30,  f: 0,     g: 17.4, h: 2.7,  i: 60,  j: 0 },
      var34: { a: 1, b: 4.4,  c: 249,  d: 0.3,  e: 37,  f: 0,     g: 18.2, h: 3.6,  i: 79,  j: 0 },
      var35: { a: 1, b: 10.4, c: 152,  d: 0.2,  e: 23,  f: 0,     g: 1.8,  h: 1.8,  i: 71,  j: 0 },
      var36: { a: 1, b: 6.7,  c: 212,  d: 0.2,  e: 31,  f: 0,     g: 9.9,  h: 3.3,  i: 50,  j: 0 },
      var37: { a: 1, b: 18.8, c: 164,  d: 0.1,  e: 26,  f: 0,     g: 1.4,  h: 1.8,  i: 0,   j: 0 },
      var38: { a: 1, b: 1.8,  c: 184,  d: 0.1,  e: 30,  f: 0.1,   g: 0.9,  h: 1.8,  i: 68,  j: 46 },
      var39: { a: 1, b: 1.7,  c: 156,  d: 0.1,  e: 24,  f: 0,     g: 1.4,  h: 2.4,  i: 57,  j: 0 },
      var40: { a: 1, b: 5.8,  c: 705,  d: 6.8,  e: 45,  f: 3.5,   g: 1,    h: 4.9,  i: 209, j: 0 },
      var41: { a: 1, b: 5.8,  c: 27,   d: 0.5,  e: 36,  f: 7.3,   g: 3.6,  h: 2.7,  i: 5,   j: 544 },
      var42: { a: 1, b: 4.9,  c: 60,   d: 0.4,  e: 30,  f: 17.4,  g: 2.5,  h: 3.5,  i: 28,  j: 498 },
      var43: { a: 1, b: 1.0,  c: 21,   d: 0.5,  e: 14,  f: 0,     g: 0.5,  h: 0,    i: 4,   j: 952 },
      var44: { a: 1, b: 2.2,  c: 40,   d: 1.1,  e: 18,  f: 11.1,  g: 3.6,  h: 1.3,  i: 10,  j: 1998 },
      var45: { a: 1, b: 2.4,  c: 138,  d: 3.7,  e: 80,  f: 69,    g: 4.3,  h: 5.8,  i: 37,  j: 862 },
      var46: { a: 1, b: 2.6,  c: 125,  d: 4.0,  e: 36,  f: 7.2,   g: 9,    h: 4.5,  i: 26,  j: 5369 },
      var47: { a: 1, b: 2.7,  c: 73,   d: 2.8,  e: 43,  f: 188.5, g: 6.1,  h: 4.3,  i: 89,  j: 608 },
      var48: { a: 1, b: 0.9,  c: 51,   d: 3.0,  e: 23,  f: 0.9,   g: 1.4,  h: 1.4,  i: 9,   j: 313 },
      var49: { a: 1, b: 0.4,  c: 27,   d: 1.1,  e: 22,  f: 112.4, g: 1.8,  h: 3.4,  i: 11,  j: 449 },
      var50: { a: 1, b: 5.8,  c: 166,  d: 3.8,  e: 59,  f: 16.6,  g: 4.7,  h: 5.9,  i: 21,  j: 1184 },
      var51: { a: 1, b: 14.3, c: 336,  d: 1.8,  e: 118, f: 6.7,   g: 29.4, h: 7.1,  i: 198, j: 2522 },
      var52: { a: 1, b: 1.1,  c: 106,  d: 0,    e: 138, f: 918.4, g: 5.7,  h: 13.8, i: 33,  j: 2755 },
      var53: { a: 1, b: 9.6,  c: 138,  d: 2.7,  e: 54,  f: 290.7, g: 8.4,  h: 5.4,  i: 83,  j: 1912 },
      var54: { a: 1, b: 3.7,  c: 20,   d: 0.4,  e: 10,  f: 21.5,  g: 0.5,  h: 1,    i: 31,  j: 196 },
      var55: { a: 1, b: 3.0,  c: 8,    d: 0.3,  e: 8,   f: 0.8,   g: 0.8,  h: 0.8,  i: 5,   j: 81 },
      var56: { a: 1, b: 2.4,  c: 16,   d: 0.4,  e: 8,   f: 2,     g: 2.8,  h: 0.8,  i: 7,   j: 399 },
      var57: { a: 1, b: 0.4,  c: 33,   d: 0.3,  e: 12,  f: 16.3,  g: 1.4,  h: 2.1,  i: 17,  j: 272 },
      var58: { a: 1, b: 1.0,  c: 54,   d: 2,    e: 65,  f: 53.9,  g: 1.6,  h: 4.3,  i: 32,  j: 431 },
      var59: { a: 1, b: 7.5,  c: 364,  d: 4,    e: 134, f: 3.5,   g: 8.3,  h: 7.7,  i: 56,  j: 0 },
      var60: { a: 1, b: 5.2,  c: 136,  d: 0.2,  e: 16,  f: 12,    g: 1.6,  h: 2.7,  i: 42,  j: 218 },
      var61: { a: 1, b: 2.3,  c: 136,  d: 0.6,  e: 45,  f: 34.9,  g: 4.9,  h: 2.5,  i: 37,  j: 370 },
      var62: { a: 1, b: 1.3,  c: 63,   d: 0.7,  e: 38,  f: 53.2,  g: 3.4,  h: 2.5,  i: 36,  j: 1253 },
      var63: { a: 1, b: 1.6,  c: 71,   d: 0.6,  e: 43,  f: 57.9,  g: 3.5,  h: 2.4,  i: 67,  j: 862 },
      var64: { a: 1, b: 8.5,  c: 87,   d: 1.7,  e: 173, f: 86.8,  g: 1.2,  h: 4.3,  i: 55,  j: 57 },
      var65: { a: 1, b: 12.8, c: 99,   d: 2.5,  e: 154, f: 85.7,  g: 3.9,  h: 4.3,  i: 65,  j: 257 },
      var66: { a: 1, b: 13.5, c: 104,  d: 2.5,  e: 136, f: 4.5,   g: 6.3,  h: 1.4,  i: 24,  j: 136 },
      var67: { a: 1, b: 20.0, c: 1367, d: 4.2,  e: 345, f: 2.9,   g: 28.7, h: 18.4, i: 162, j: 0 },
      var68: { a: 1, b: 17.4, c: 1055, d: 3.7,  e: 459, f: 5.1,   g: 26.9, h: 38.2, i: 93,  j: 0 },
      var69: { a: 1, b: 26.9, c: 1691, d: 11.4, e: 792, f: 0,     g: 38.4, h: 24.6, i: 217, j: 0 },
      var70: { a: 1, b: 0,    c: 0,    d: 0,    e: 0,   f: 0,     g: 4,    h: 5.1,  i: 50,  j: 0 },
      var71: { a: 1, b: 0,    c: 0,    d: 0,    e: 0,   f: 0,     g: 0,    h: 2.3,  i: 42,  j: 0 },
      var72: { a: 1, b: 8.7,  c: 237,  d: 3,    e: 72,  f: 0,     g: 2,    h: 11.9, i: 40,  j: 0 },
      var73: { a: 1, b: 8.0,  c: 77,   d: 1.3,  e: 39,  f: 0,     g: 0.9,  h: 3.4,  i: 14,  j: 0 },
      var74: { a: 1, b: 34.9, c: 0,    d: 0,    e: 0,   f: 0,     g: 0,    h: 0,    i: 0,   j: 0 },
      var75: { a: 1, b: 14.7, c: 0,    d: 0.5,  e: 74,  f: 0,     g: 0,    h: 0,    i: 5,   j: 0 },
      var76: { a: 1, b: 9.0,  c: 0,    d: 10.3, e: 244, f: 0,     g: 1.9,  h: 7.5,  i: 146, j: 0 },
      var77: { a: 1, b: 6.4,  c: 11,   d: 0.4,  e: 7,   f: 0.2,   g: 0.2,  h: 0.4,  i: 3,   j: 0 },
    },
    ints: {
      var1: 1,
      var2: 1,
      var3: 1,
      var4: 1,
      var5: 1,
      var6: 1,
      var7: 1,
      var8: 1,
      var9: 1,
      var10: 1,
      var11: 1,
      var12: 1,
      var13: 1,
      var14: 1,
      var15: 1,
      var16: 1,
      var17: 1,
      var18: 1,
      var19: 1,
      var20: 1,
      var21: 1,
      var22: 1,
      var23: 1,
      var24: 1,
      var25: 1,
      var26: 1,
      var27: 1,
      var28: 1,
      var29: 1,
      var30: 1,
      var31: 1,
      var32: 1,
      var33: 1,
      var34: 1,
      var35: 1,
      var36: 1,
      var37: 1,
      var38: 1,
      var39: 1,
      var40: 1,
      var41: 1,
      var42: 1,
      var43: 1,
      var44: 1,
      var45: 1,
      var46: 1,
      var47: 1,
      var48: 1,
      var49: 1,
      var50: 1,
      var51: 1,
      var52: 1,
      var53: 1,
      var54: 1,
      var55: 1,
      var56: 1,
      var57: 1,
      var58: 1,
      var59: 1,
      var60: 1,
      var61: 1,
      var62: 1,
      var63: 1,
      var64: 1,
      var65: 1,
      var66: 1,
      var67: 1,
      var68: 1,
      var69: 1,
      var70: 1,
      var71: 1,
      var72: 1,
      var73: 1,
      var74: 1,
      var75: 1,
      var76: 1,
      var77: 1,
    },
  };

results = solver.Solve(model, null, false, true);
console.log(results);

And I get the following result:

{ feasible: true,
  result: -Infinity,
  bounded: false,
  var51: 1304362.00000001,
  var70: 1.6352736395150337e+36,
  var50: 248520187561305870000,
  var4: 184035095172865620000,
  var74: 131707210,
  var71: 312,
  var8: 114959664727729370000,
  var36: 1.63550998754279e+21,
  var59: 1349766,
  var64: 2.841424062706935e+21,
  var44: 250051195134805930000,
  var34: 5.138361402535557e+21,
  var30: 5.927771967540281e+21,
  var43: 6.170253407483763e+21 }

Especially strange thing is that I've got -Infinity for b while I have a constraint for that attribute —b: { min: 1000 } — and it's being checked (the last argument to the Solve function).

The second issue I've got when eliminated a constraint from the above example — I'm still waiting for result. Does it hang?

Solver Freezes?

I'm building an app using ionic framework and my model has about 500 constraints and 5000 variables.

Here is the file model attached.
model.txt

When I try to solve it, using solver.Solve(model), for exemple. The page freezes on my local environment. Is there any work around? Am I doing something wrong?

Thanks a lot :)

Solutions in Integer Mode

Hi,
basically, what I want to accomplish is to get all the solutions in the integer mode, not only the optimal solution.
Is this function/method supported by your solver?
Thank you for your answer in advance!
Have a great day,
Alex

Typo in markdown

results = solver.solve(model)

should be

results = solver.Solve(model);

Objective values coming back negative

not sure why, but on some problems, the objective value comes back as a negative number. For instance, this throws up a -1460 as result. Any ideas why?

{
        "name": "Integer Clothing Shop Problem II",
        "optimize": "profit",
        "opType": "max",
        "constraints": {
            "yards": {
                "max": 150
            },
            "hours": {
                "max": 200
            }
        },
        "variables": {
            "coat": {
                "hours": 10,
                "yards": 3,
                "profit": 50,
                "coat": 1
            },
            "pants": {
                "hours": 4,
                "yards": 5,
                "profit": 40,
                "pants": 1
            },
            "hat": {
                "hours": 12,
                "yards": 1,
                "profit": 10,
                "hat": 1
            },
            "socks": {
                "hours": 0.5,
                "yards": 0.5,
                "profit": 0.5,
                "socks": 1
            }
        },
        "ints": {
            "coat": 1,
            "pants": 1,
            "hat": 1,
            "socks": 1
        },
        "expects": {
            "feasible": true,
            "coat": 10,
            "pants": 24,
            "hat": 0,
            "result": 1460
        }
    }

API suggestion

Hello,
I was searching for a JS MILP Solver and here it is!

Actually I was wondering if adding function API for constraint system definition was planned?

Let me explain: I created a library (named constrained) to textually define linear optimization problems. It also links JavaScript object properties to the optimization problem variables, so that the user do not have to manually write the code that will update its javascript objects. It also handle event emission on variable value and optimal solution change (because problem definitions can be changed dynamically).

I am now using cassowary but it does not seem maintained, it does not handle integer variables and the code is difficult to understand. Its advantage however, is that it has a function API for defining constraint systems:

var solver = new c.SimplexSolver();
var x = new c.Variable({ value: 167 });
var y = new c.Variable({ value: 2 });
var eq = new c.Equation(x, new c.Expression(y));
solver.addConstraint(eq);

That makes it easy to use by another library (such as mine). I was wondering if you would accept PR that would add such an API?

By the way, by looking at your source code I think that such an API improvement could go hand in hand with performance improvement of the MILP solver. It looks like you implemented a branch and cut, which is nice, but I think that one of the performance drawbacks of the implementation is the use of JSON.parse and JSON.stringify to make copies of the problem.

Thank you for your time

How can we optimize with the bag

Hi All,

I have a small doubt regarding linear programming (jsLPSolver)

var solver = require("./src/solver"),
model =
{
"optimize": "price",
"constraints": {
"carb": {"equal": 80}, // 80 is in kg
"protein": {"equal": 25},
"fat": {"equal": 21}
},
"variables": {
// Format
// Fruit {contents : % ..... , price of each fruit}.
"Product1": {"carb": .46,"protein": 0.0,"fat": 0.0,"vita": 0.0, "iron": 0.0,"price":5.94,"Product1":1},
"Product2": {"carb": .10,"protein": .18,"fat": 0,"vita": 0, "iron": 0.0,"price":7.0,"Product2":1},
"Product3": {"carb": .18,"protein": .46,"fat": 0,"vita": 0, "iron": 0.0,"price":24.0,"Product3":1},
"Product4": {"carb": 0,"protein": 0,"fat": .60,"vita": 0.0, "iron": 0.0,"price":11.14,"Product4":1}
}
}

console.log(solver.Solve(model));

Results :
{ feasible: true,
result: 2215.8178744
bounded: true,
Product1: 143.71980676,
Product2: 138.88888889,
Product4: 35
}


DETAILS:
Let say we have 4 different bags in which we can put max 50 kg of these products (Product1 / Product2 / Product3 / Product4)
i.e Bag1 contains 50kg of Product1, Bag2 contains 50kg of Product2 and so on..

Bag1 price is 247 (50 * 5.94)
Bag2 price is 350 (50 * 7)
Bag3 price is 1200 (50 * 24)
Bag4 price is 557 (50 * 11.14)

HOW TO MODIFY:
In the above case we are dealing with the minimum price solution with the given constraints.

In the above results I need to increase / decrease the quantities (Products ) in such a way that minimum cost is involved.
As the total actual cost would be (by the above results)
3 Bags of Product1, 3 bags of Product2, 1 Bag of Product4 (The fruits does come is 50kg pack )

=(3 * 247 + 3 * 350 +1 *557 )

How to make the solution more better by keeping it such a way that involves min cost but keeping the same constraints.

Brice can you please help me out.
Thanks

Quick Question

If I have 10 players, and each player has variables, how can get the top 5 players that meet the constraints?

var model = {
      optimize: 'salary',
      opType: 'max',
      constraints: {
        salary: {max: 50000}
      },
      variables: {
        p0: {weight: 10, salary: 1000},
        p1: {weight: 20, salary: 2000},
        p2: {weight: 30, salary: 3000},
        p3: {weight: 40, salary: 4000},
        p4: {weight: 50, salary: 5000},
        p5: {weight: 60, salary: 6000},
        p6: {weight: 70, salary: 7000},
        p7: {weight: 80, salary: 8000},
        p8: {weight: 90, salary: 9000},
        p9: {weight: 100, salary: 10000}
      }
    };

lp-solve format to json format

Hi, thanks for the solver which is one of the very few in Javascript. I am working on a problem and the following (in LP-solve format) was solved successfully

          var model = [
                               "min: 1 d_cred 869.83 d_salespct -632.36 d_rmpct -1 d_recv -1 d_rm -158.21",

                               "1 d_salespct <= 1.2",
			  "1 d_salespct >= 1.05",
			  "1 d_rmpct <= 1.3",
			  "1 d_rmpct >= 0.8",
			  "12 d_cred -316.18 d_rmpct >= 0",
			  "-12 d_cred 1897.08 d_rmpct >= 0",
			  "12 d_recv -434.92 d_salespct >= 0",
			  "-12 d_recv 2609.49 d_salespct >= 0",
			  "12 d_rm -316.18 d_rmpct >= 0",
			  "-12 d_rm 1897.08 d_rmpct >= 0",
			  "12.5 d_clwip -316.18 d_rmpct -79.57 >= 0",
			  "-15 d_clwip 1897.08 d_rmpct 477.45 >= 0",
			  "12.5 d_clfg 0.5 d_clwip -316.18 d_rmpct -87.12 >= 0",
			  "-15 d_clfg -3 d_clwip 1897.08 d_rmpct 522.75 >= 0",
			  "1 d_cred 869.83 d_salespct -632.36 d_rmpct -1 d_recv -1 d_rm -158.21 = 0"
                              ];

However this format is for testing purpose only since the values cannot be pre-fed and will be dynamically generated. So I want to use the json format. But I am stuck on how to turn this into json format. Following is how I have framed it:

 var model = {
    "optimize": {
            "d_cred": 1,
            "d_salespct": 869.83,
            "d_rmpct": -632.36,
            "d_recv": -1,
			"d_rm": -1,
			"const": -158.21
        },
    "opType": "min",
    "constraints": {
        "d_salespct": {"max": 1.2, "min": 1.05},
        "d_rmpct": {"max": 1.3, "min": 0.8},

"variables": {
        		"d_salespct": {
						"d_salespct": 1
		                             },
			"d_rmpct":   {
					       "d_rmpct": 1
		                            },
			"d_cred":     {
			   		      "d_cred": 1
		                            },
			"d_recv":    {
					    "d_recv": 1
		                          },
			"d_rm":      {
					"d_rm": 1
		                           },
			"d_clwip": {
					"d_clwip": 1
		                        },
			"d_clfg": {
					"d_clfg": 1
		                      }


I am able to frame simple constraints like "1 d_salespct <= 1.2", but for other constraints I am not able to figure out how to represent them in json format. Can you please help?

Solution Variables being dropped

The solver is dropping solution variables for an unknown reason. e.g. a mwe:

var model = { optimize: 'total_z',
  opType: 'min',
  constraints: 
   { c_0_1: { min: 2 },
     c_0_2: { min: 2 },
     c_1_2: { min: 2 },
     a_0: { min: 1 },
     b_0: { min: -1 },
     a_1: { min: 2 },
     b_1: { min: -2 },
     a_2: { min: 3 },
     b_2: { min: -3 } },
  variables: 
   { y_0: { a_0: 1, b_0: -1, c_0_1: -1, c_0_2: -1 },
     z_0: { a_0: 1, b_0: 1, total_z: 1 },
     y_1: { a_1: 1, b_1: -1, c_0_1: 1, c_1_2: -1 },
     z_1: { a_1: 1, b_1: 1, total_z: 1 },
     y_2: { a_2: 1, b_2: -1, c_0_2: 1, c_1_2: 1 },
     z_2: { a_2: 1, b_2: 1, total_z: 1 } } };
console.log(solver.Solve(model));
// { feasible: true, result: 2, y_1: 2, z_2: 1, y_2: 4, z_0: 1 };

For some reason 'y_0' = 0 is missing from the result.

Memory leak / Solver freezing

It's odd. I can't tell if this is a memory leak issue or a solver issue. It appears that enabling Ints for these variables causes the solver to slip into an infinite loop.

Other times, if the constraints are a little different, it will solve it within 4 seconds; however, if I run again, it will go into an infinite loop (or so it appears).

Below is the JSON-stringify version of the model:

{"optimize":"totalCost","opType":"max","variables":{"Tanks":{"Dollers":95,"Goats":75,"Land":40,"Air":40,"totalCost":210},"Aircrafts":{"Dollers":145,"Goats":70,"Land":85,"Air":40,"totalCost":300},"Cars":{"Dollers":130,"Goats":120,"Land":170,"Air":70,"totalCost":420},"Horses":{"Dollers":370,"Goats":270,"Land":290,"Air":75,"totalCost":930},"Motorcycles":{"Dollers":450,"Goats":515,"Land":480,"Air":80,"totalCost":1445},"Dogs":{"Dollers":1000,"Goats":300,"Land":350,"Air":70,"totalCost":1650}},"constraints":{"Dollers":{"max":39599},"Goats":{"max":37025},"Land":{"max":38234},"Air":{"max":66400}},"ints":{"Tanks":1,"Aircrafts":1,"Cars":1,"Horses":1,"Motorcycles":1,"Dogs":1}}

Running in Browser

Hello,

This is probably a rather basic issue, but I am new to javascript. I am wondering if there is a way to make this library work with javascript running solely in a browser? I am running into issues with the many "require()" statements. I have tried bundling everything into a single file using Browserify, but still no luck.

Any help would be greatly appreciated!

More complicated objective function?

@bchevalier asked in my other issue thread if I had feedback on using this module. Making a new ticket just to keep things clean.

Stable so far, but then I discovered my use case is a bit beyond the scope of this library. I am not even sure if this is possible using the simplex method (or whatever algorithm you are using), but I thought I would at least bring it up.

Essentially, I discovered that my particular application required a solution near the center of the solution set. In a perfect scenario, I could specify an ideal ratio between two of my variables, and then have the solver help me find the closest possible solution to that ratio. Reorganized as a minimization objective, my scenario looked like this: MIN( ABS( variableA / variableB - optimalRatio ) ). I could also reorganize a bit to get that ABS out of there, but I wouldn't be sure where to go from there.

Is there any way to accomplish this with this library? I did notice the ReformatLP method that seems to give some advanced functionality, but I haven't dug into it.

failing tests

The following 2 tests in the test suite are currently failing. Not sure if an issue or not.

   // 1) The Solve method takes a problem and solves it should be able to solve the Wiki 1:
    /*Returned*/
    { 
        feasible: 1, 
        result: Infinity, 
        z: 10 
    } 

    /*Expected*/
    { 
        feasible: 1, 
        result: 18.57143, 
        x: 2.14286, 
        y: 0, 
        z: 3.57143 
    }


  // 2) The Solve method takes a problem and solves it should be able to solve the Integer Berlin Air Lift Problem:
    /*Returned*/    
    { 
        brit: 12, 
        feasible: 1, 
        result: 1020000, 
        yank: 26 
    } 

    /*Expected*/
    { 
        brit: 12, 
        feasible: 1, 
        result: 960000, 
        yank: 24 
    }

Solver freezes at given model

Hi,

I have been using jsLPSolver quite a lot and it normally works as it's supposed to, but I came up with a model "by accident", where the solver simply freezes and doesn't return a result.

Is my model corrupt or is there a bug in the solver?

This is my model:
https://pastebin.com/XWY0Kqpx

Thank you in advance!

Make the process asynchronous

When running in browser, it happens every now and then that the process freezes. I am not sure if this is about the model to solve, or the code that solves it. Anyway, I was wondering an asynchronous process as fail safe will be nice, better if it is stoppable and callbacks per progress, in order to vet the progress data.
I am not sure if this is too much to ask for, thank you for your work anyway!

Constraints for sum and relations of the variables

Hello! Firstly, thank you for the library, it's just amazing.

I'm somehow new to Javascript and of course to jsLPSolver and I have to solve the following equation:

    Objective: Maximize r17
    
    Variables:
    - Floats: q7, q8, q9, q10, q11, q12, q13, q14, q15, q16, q17
    - Floats: r7, r8, r9, r10, r11, r12, r13, r14, r16, r16, r17

    Relations:
    - q7 +q8 +q9 +q10 + q11 + q12 + q13 + q14 + q15 + q16 = q17
    - r7 +r8 +r9 +r10 + r11 + r12 + r13 + r14 + r15 + r16 = r17
    - r7 = q7 / 0.50
    - r8 = q8 / 0.75
    - r9 = q9 / 0.88
    - r10 = q10 / 1.00
    - r11 = q11 / 1.13
    - r12 = q12 / 1.18
    - r13 = q13 / 1.76
    - r14 = q14 / 2.11
    - r15 = q15 / 2.33
    - r16 = q16 / 2.50

    Constraints:
    - 1000 <= q7 <= 17500
    - 2000 <= q8 <= 6300
    - 5000 <= q9 <= 19292
    - 1200 <= q10 <= 29820
    - 100 <= q11 <=2887.50
    - 700 <= q12 <= 20825
    - 300 <= q13 <= 9481.50
    - 150 <= q14 <= 5764.50,
    - 1200 <= q15 <= 48594,
    - 300 <= q16 <= 12600 
    - 60500 <= q17 <= 60500

Basically, I don't have any idea right now how could I plug in these rules into the library's model. Like the relations or the sum constraints?

Publish in npmjs

Any reason why you have not published this in npmjs...that way I can ensure the latest version gets used automatically in my nodejs app

Working with webworkers

First of all thank you very much for the solver! It is one of the few available in javascript & it is also very simple to use.

I'm trying to run the solver in a web worker but unfortunatly without success till now.
Could you add a simple example of usage with web workers?

Thank you in advance and again for the solver;)

Need help to get approximate int results

Hi, I have a problem of 114 constraints x 291 variables, where all the variables should be integer.
The non-integer problem is solved in <100ms, while the integer problem cannot be solved in five minutes (at least 5000 times slower).

Is there any way to find approximate integer results in a short time? (e.g. 100 times slow down at current problem size)
By approximate, I mean that I want to avoid lots of variables with fraction +-0.5, but it is OK if most (do not need all) variables have fraction of +-0.05.

If there are some parameters to control it, I could tune them for my problem.
However I didn't find any apparent ones. The precision argument in Solve() doesn't seem to work for integers.

u.txt -> u.js and run with node (at jsLPSolver/..)
u.txt
model.txt

Implementation of the revised simplex

It implies the implementation of the LU decomposition method.

This should improve resolution times on most problems (especially on mix integer problems) and will dramatically reduce errors due to numerical instability of the traditional simplex.

Most of the details of the algorithm are on this slides: http://faculty.chicagobooth.edu/kipp.martin/root/htmls/coursework/36900/handouts/simplex2.pdf
(What is missing though is how to dynamically update the LU decomposition matrix instead of computing it from scratch at every iteration).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.