├── AUTHORS ├── .gitignore ├── htmldocs ├── styles │ ├── help.png │ ├── printer.png │ ├── magnifier.png │ ├── page_white_code.png │ ├── page_white_copy.png │ └── shThemeDefault.css ├── scripts │ ├── clipboard.swf │ └── shBrushLua.js └── default.css ├── papers ├── Rima-ORSNZ-paper-2010.pdf ├── Rima-ORSNZ-presentation-2010.pdf └── Rima-INFORMS-presentation-2010.pdf ├── docs ├── footer.html ├── header.html ├── features.txt ├── differentiation.txt ├── contents.txt ├── cases.txt ├── nonlinear.txt ├── index.txt ├── arrays.txt ├── simple_lp.txt ├── sums.txt ├── expressions.txt ├── structures.txt ├── blending.txt ├── functions.txt ├── references.txt ├── install.txt └── knapsack.txt ├── expressions ├── simple_math_ops.txt ├── index.txt ├── mul.txt ├── add.txt └── sum.txt ├── config.ld ├── INSTALL ├── lua ├── rima-test-solvers.lua ├── rima │ ├── types.lua │ ├── solvers.lua │ ├── lib │ │ ├── proxy.lua │ │ ├── trace.lua │ │ └── object.lua │ ├── types │ │ └── undefined_t.lua │ ├── solvers │ │ ├── cbc.lua │ │ ├── clp.lua │ │ ├── lpsolve.lua │ │ ├── linear.lua │ │ └── ipopt.lua │ ├── operators │ │ ├── minmax.lua │ │ ├── mod.lua │ │ ├── call.lua │ │ ├── pow.lua │ │ ├── case.lua │ │ ├── math.lua │ │ ├── add_mul.lua │ │ ├── sum.lua │ │ └── product.lua │ ├── operations.lua │ ├── operator.lua │ ├── compiler.lua │ ├── closure.lua │ ├── sets.lua │ ├── mp │ │ ├── constraint.lua │ │ └── linearise.lua │ ├── func.lua │ ├── sets │ │ ├── element.lua │ │ └── list.lua │ ├── expression.lua │ ├── lib.lua │ └── core.lua ├── tests │ ├── rima │ │ ├── lib.lua │ │ ├── lib │ │ │ ├── proxy.lua │ │ │ └── object.lua │ │ ├── operators │ │ │ ├── mul.lua │ │ │ ├── math.lua │ │ │ ├── product.lua │ │ │ ├── mod.lua │ │ │ ├── add.lua │ │ │ ├── pow.lua │ │ │ ├── case.lua │ │ │ ├── minmax.lua │ │ │ └── call.lua │ │ ├── sets │ │ │ ├── ref.lua │ │ │ └── element.lua │ │ ├── mp │ │ │ └── constraint.lua │ │ ├── core.lua │ │ ├── differentiation.lua │ │ ├── address.lua │ │ ├── expression.lua │ │ ├── types │ │ │ └── undefined_t.lua │ │ ├── index.lua │ │ └── mp.lua │ └── test │ │ └── series.lua ├── examples │ ├── nonlinear.lua │ ├── simple.lua │ ├── minimax.lua │ └── sudoku.lua ├── rima-test.lua ├── test │ ├── directory.lua │ ├── strict.lua │ └── series.lua └── rima.lua ├── update-copyright.sh ├── CHANGELOG ├── LICENSE ├── c └── rima_solver_tools.h ├── ROADMAP.md ├── rima-0.03-1.rockspec ├── README.md └── makefile /AUTHORS: -------------------------------------------------------------------------------- 1 | Leyland, Geoff B. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Thumbs.db 3 | *.so 4 | srcdocs 5 | 6 | -------------------------------------------------------------------------------- /htmldocs/styles/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoffleyland/rima/HEAD/htmldocs/styles/help.png -------------------------------------------------------------------------------- /htmldocs/styles/printer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoffleyland/rima/HEAD/htmldocs/styles/printer.png -------------------------------------------------------------------------------- /htmldocs/scripts/clipboard.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoffleyland/rima/HEAD/htmldocs/scripts/clipboard.swf -------------------------------------------------------------------------------- /htmldocs/styles/magnifier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoffleyland/rima/HEAD/htmldocs/styles/magnifier.png -------------------------------------------------------------------------------- /papers/Rima-ORSNZ-paper-2010.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoffleyland/rima/HEAD/papers/Rima-ORSNZ-paper-2010.pdf -------------------------------------------------------------------------------- /htmldocs/styles/page_white_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoffleyland/rima/HEAD/htmldocs/styles/page_white_code.png -------------------------------------------------------------------------------- /htmldocs/styles/page_white_copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoffleyland/rima/HEAD/htmldocs/styles/page_white_copy.png -------------------------------------------------------------------------------- /papers/Rima-ORSNZ-presentation-2010.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoffleyland/rima/HEAD/papers/Rima-ORSNZ-presentation-2010.pdf -------------------------------------------------------------------------------- /papers/Rima-INFORMS-presentation-2010.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoffleyland/rima/HEAD/papers/Rima-INFORMS-presentation-2010.pdf -------------------------------------------------------------------------------- /docs/footer.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | -------------------------------------------------------------------------------- /expressions/simple_math_ops.txt: -------------------------------------------------------------------------------- 1 | E $a * $b + $c * $d 2 | P a*b + c*d 3 | S b = 3 4 | s d = 5 5 | P 3*a + 5*c 6 | 7 | E 2 + 3*$a 8 | P 2 + 3*a 9 | S a = 5 10 | P 17 11 | -------------------------------------------------------------------------------- /config.ld: -------------------------------------------------------------------------------- 1 | project = "Rima" 2 | title = "Rima Source Documentation" 3 | description = "Rima is a tool for defining systems of equations" 4 | all = true 5 | file = "lua" 6 | format = "markdown" 7 | dir = "srcdocs" -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | See htmldocs/install.html for instructions to install Rima. 2 | If you got this copy of Rima from version control and you haven't built the 3 | documentation, see docs/install.txt. 4 | 5 | Install instructions for the latest release can also be found at 6 | http://rima.incremental.co.nz/install.html -------------------------------------------------------------------------------- /expressions/index.txt: -------------------------------------------------------------------------------- 1 | E $a 2 | P a 3 | D index(address{"a"}) 4 | S a = 5 5 | P 5 6 | S a = $b 7 | P b 8 | D index(address{"b"}) 9 | s b = 6 10 | P 6 11 | 12 | E $a.b 13 | P a.b 14 | D index(address{"a", "b"}) 15 | S a = { b = 2 } 16 | P 2 17 | S a = { b = $c.d } 18 | P c.d 19 | s c = { d = 5 } 20 | P 5 21 | 22 | -------------------------------------------------------------------------------- /lua/rima-test-solvers.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | dofile("../lua/examples/simple.lua") 5 | dofile("../lua/examples/whiskas.lua") 6 | dofile("../lua/examples/minimax.lua") 7 | dofile("../lua/examples/knapsack.lua") 8 | dofile("../lua/examples/assignment.lua") 9 | dofile("../lua/examples/sudoku.lua") 10 | dofile("../lua/examples/nonlinear.lua") 11 | -------------------------------------------------------------------------------- /lua/rima/types.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local require = require 5 | 6 | module(...) 7 | 8 | -------------------------------------------------------------------------------- 9 | 10 | require("rima.types.undefined_t") 11 | require("rima.types.number_t") 12 | 13 | 14 | -- EOF ------------------------------------------------------------------------- 15 | -------------------------------------------------------------------------------- /update-copyright.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | YEAR=${1:-2011} 4 | 5 | for suffix in lua cpp txt html 6 | do 7 | for f in `find . -name "*.$suffix"` 8 | do 9 | echo $f 10 | cat $f | \ 11 | sed -E "s/Copyright([^0-9]+)(20[0-9][0-9])\-(20[0-9][0-9])/Copyright\1\2-$YEAR/" | \ 12 | sed -E "s/Copyright([^0-9]+)(20[0-9][0-9])([^\-])/Copyright\1\2-$YEAR\3/" | \ 13 | sed "s/license.txt/LICENSE/" > \ 14 | atempfile 15 | mv atempfile $f 16 | done 17 | done 18 | 19 | -------------------------------------------------------------------------------- /lua/rima/solvers.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local require = require 5 | 6 | module(...) 7 | 8 | 9 | -------------------------------------------------------------------------------- 10 | 11 | require("rima.solvers.clp") 12 | require("rima.solvers.cbc") 13 | require("rima.solvers.lpsolve") 14 | require("rima.solvers.ipopt") 15 | 16 | 17 | -- EOF ------------------------------------------------------------------------- 18 | 19 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | Rima 0.05 (2011-03-28) 2 | Support for nonlinear problems with ipopt 3 | Automatic differentiation 4 | Compiling expressions to Lua code 5 | 6 | Rima 0.04 (2010-10-29) 7 | Submodel support! 8 | 9 | Rima 0.03 (2010-02-22) 10 | Better syntax for array assignments 11 | Constraints are named and part of the model structure 12 | 13 | Rima 0.02 (2009-10-22) 14 | More comprehensive table handling 15 | Syntax improvements 16 | Improved documentation 17 | 18 | Rima 0.01 (2009-05-20) 19 | First public release. Just enough to formulate a small LP -------------------------------------------------------------------------------- /expressions/mul.txt: -------------------------------------------------------------------------------- 1 | E 0 * $a 2 | P 0 3 | 4 | E 1 * $a 5 | P a 6 | D index(address{"a"}) 7 | 8 | 9 | E 2 * $a 10 | P 2*a 11 | D *(2^1, index(address{"a"})^1) 12 | S a = 5 13 | P 10 14 | S a = $b 15 | D *(2^1, index(address{"b"})^1) 16 | P 2*b 17 | s b = 7 18 | P 14 19 | 20 | E 3 * $a / 3 21 | P a 22 | 23 | E $a * $a 24 | P a^2 25 | 26 | E $a / $a 27 | P 1 28 | 29 | E $a * $b 30 | P a*b 31 | S a = 5 32 | D *(5^1, index(address{"b"})^1) 33 | P 5*b 34 | S b = 6 35 | P 6*a 36 | S a = 7 37 | s b = 8 38 | P 56 39 | 40 | -------------------------------------------------------------------------------- /lua/rima/lib/proxy.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local setmetatable = setmetatable 5 | 6 | module(...) 7 | 8 | 9 | -- Types ----------------------------------------------------------------------- 10 | 11 | local proxy = _M 12 | local objects = setmetatable({}, { __mode = "k" }) 13 | 14 | 15 | function proxy:new(o, class) 16 | local p = setmetatable({}, class) 17 | objects[p] = o 18 | return p 19 | end 20 | 21 | 22 | function proxy.O(p) 23 | return objects[p] or p 24 | end 25 | 26 | 27 | -- EOF ------------------------------------------------------------------------- 28 | 29 | -------------------------------------------------------------------------------- /htmldocs/default.css: -------------------------------------------------------------------------------- 1 | body 2 | { 3 | font-family: Helvetica, Arial, Verdana, sans-serif; 4 | font-size: 12px; 5 | margin: 15px 15px 15px 15px; 6 | } 7 | 8 | pre 9 | { 10 | margin: 0px 20px 0px 20px; 11 | } 12 | 13 | code 14 | { 15 | font-family: "Courier New", Courier, monospace !important; 16 | } 17 | 18 | #footer 19 | { 20 | text-align: right; 21 | font-size: 80%; 22 | font-style: italic; 23 | margin-top: 1px; 24 | padding: 5px 2px 2px 2px; 25 | width: 100%; 26 | } 27 | 28 | hr 29 | { 30 | border-style: solid; 31 | margin: 0px 0px 0px 0px; 32 | padding: 0px 0px 0px 0px; 33 | border-width: 1px 0px 0px 0px; 34 | border-color: gray; 35 | } 36 | -------------------------------------------------------------------------------- /lua/tests/rima/lib.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local lib = require("rima.lib") 5 | 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | return function(T) 10 | local R = lib.repr 11 | local D = lib.dump 12 | 13 | -- repr 14 | T:check_equal(R(1), 1) 15 | T:check_equal(R("a"), "a") 16 | T:check_equal(R("nil"), "nil") 17 | 18 | -- dump 19 | T:check_equal(D(1), "1") 20 | T:check_equal(D("a"), '"a"') 21 | T:check_equal(D(nil), "nil") 22 | end 23 | 24 | 25 | ------------------------------------------------------------------------------ 26 | 27 | -------------------------------------------------------------------------------- /lua/tests/rima/lib/proxy.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local proxy = require("rima.lib.proxy") 5 | local object = require("rima.lib.object") 6 | 7 | 8 | ------------------------------------------------------------------------------ 9 | 10 | return function(T) 11 | local my_type = object:new_class({}, "my_type") 12 | 13 | local p = proxy:new({}, my_type) 14 | T:test(object.typeinfo(p).my_type, "typeinfo(p).my_type") 15 | T:check_equal(object.typename(p), "my_type", "typename(proxy) == 'my_type'") 16 | 17 | local o = {} 18 | local p = proxy:new(o, my_type) 19 | T:check_equal(proxy.O(p), o) 20 | T:check_equal(proxy.O(o), o) 21 | end 22 | 23 | 24 | ------------------------------------------------------------------------------ 25 | 26 | -------------------------------------------------------------------------------- /lua/tests/rima/operators/mul.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local ops = require("rima.operations") 5 | 6 | local object = require("rima.lib.object") 7 | local index = require("rima.index") 8 | 9 | 10 | ------------------------------------------------------------------------------ 11 | 12 | return function(T) 13 | local A = ops.mul(3, index:new(nil, "a")) 14 | 15 | -- constructors 16 | T:test(object.typeinfo(A).mul, "typeinfo(mul).mul") 17 | T:check_equal(object.typename(A), "mul", "typename(mul)=='mul'") 18 | 19 | -- string representation 20 | T:check_equal(A, "3*a") 21 | 22 | local B = ops.mul(0, 2) 23 | T:check_equal(B, 0) 24 | end 25 | 26 | 27 | ------------------------------------------------------------------------------ 28 | 29 | -------------------------------------------------------------------------------- /lua/tests/rima/operators/math.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local lib = require("rima.lib") 5 | local interface = require("rima.interface") 6 | 7 | 8 | ------------------------------------------------------------------------------ 9 | 10 | return function(T) 11 | local E = interface.eval 12 | local D = lib.dump 13 | local R = interface.R 14 | local opmath = interface.math 15 | 16 | local a, b = R"a, b" 17 | 18 | T:check_equal(opmath.exp(1), math.exp(1)) 19 | 20 | T:check_equal(D(opmath.exp(a)), 'expression(exp(index(address{"a"})))') 21 | local S = {} 22 | T:check_equal(E(opmath.exp(a), S), "exp(a)") 23 | S.a = 4 24 | T:check_equal(E(opmath.sqrt(a), S), 2) 25 | end 26 | 27 | 28 | ------------------------------------------------------------------------------ 29 | 30 | -------------------------------------------------------------------------------- /lua/tests/rima/sets/ref.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local ref = require("rima.sets.ref") 5 | 6 | local lib = require("rima.lib") 7 | local scope = require("rima.scope") 8 | local interface = require("rima.interface") 9 | 10 | 11 | ------------------------------------------------------------------------------ 12 | 13 | return function(T) 14 | local E = interface.eval 15 | local D = lib.dump 16 | local R = interface.R 17 | 18 | do 19 | local a, A, b, c = R"a, A, b, c" 20 | local S = scope.new{A={{x="onedotx"}}} 21 | S.b[{a=A}] = a 22 | S.c[{a=A}] = A[a] 23 | T:check_equal(E(b[1].x, S), "onedotx") 24 | T:check_equal(E(c[1].x, S), "onedotx") 25 | end 26 | end 27 | 28 | 29 | ------------------------------------------------------------------------------ 30 | 31 | -------------------------------------------------------------------------------- /expressions/add.txt: -------------------------------------------------------------------------------- 1 | E 1 + 2 2 | P 3 3 | S 4 | P 3 5 | 6 | E 0 + $a 7 | P a 8 | D index(address{"a"}) 9 | 10 | E 1 + $a 11 | P 1 + a 12 | D +(1*1, 1*index(address{"a"})) 13 | S a = 5 14 | P 6 15 | S a = $b 16 | D +(1*1, 1*index(address{"b"})) 17 | P 1 + b 18 | s b = 7 19 | P 8 20 | 21 | E 1 + $a - 1 22 | P a 23 | 24 | E $a + $a 25 | P 2*a 26 | 27 | E - $a 28 | P -a 29 | S a = 5 30 | P -5 31 | 32 | E $a - $a 33 | P 0 34 | 35 | E $a + $b 36 | P a + b 37 | S a = 5 38 | D +(1*5, 1*index(address{"b"})) 39 | P 5 + b 40 | S b = 6 41 | P 6 + a 42 | S a = 7 43 | s b = 8 44 | P 15 45 | 46 | E $a + $b + $c 47 | P a + b + c 48 | S 49 | D +(1*index(address{"a"}), 1*index(address{"b"}), 1*index(address{"c"})) 50 | 51 | E 2 - (3 + $a) 52 | P -1 - a 53 | S a = 5 54 | P -6 55 | -------------------------------------------------------------------------------- /lua/rima/types/undefined_t.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local object = require("rima.lib.object") 5 | local lib = require("rima.lib") 6 | 7 | module(...) 8 | 9 | 10 | -- Undefined (base) Type ------------------------------------------------------- 11 | 12 | local undefined_t = object:new_class(_M, "undefined_t") 13 | 14 | 15 | -- String representation ------------------------------------------------------- 16 | 17 | function undefined_t:__repr(format) 18 | return "undefined" 19 | end 20 | __tostring = lib.__tostring 21 | 22 | 23 | function undefined_t:describe(s, format) 24 | return ("%s undefined"):format(lib.repr(s, format)) 25 | end 26 | 27 | 28 | function undefined_t:includes(v, env) 29 | return true 30 | end 31 | 32 | 33 | -- EOF ------------------------------------------------------------------------- 34 | 35 | -------------------------------------------------------------------------------- /docs/header.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | TITLE 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /docs/features.txt: -------------------------------------------------------------------------------- 1 | --! env rima = require("rima") 2 | 3 | # Rima Features 4 | 5 | This page gives a quick overview of Rima's features, 6 | not an introduction to using and understanding it. 7 | For that, read the [user guide](contents.html). 8 | 9 | 10 | ## Symbolic Expressions 11 | 12 | Rima handles expressions and equations symbolically. 13 | 14 | You can define a sum over a set X 15 | 16 | x, X, a, b, c = rima.R"x, X, a, b, c" 17 | e = rima.sum{x=X}(x^2) 18 | = e --> sum{x in X}(x^2) 19 | = rima.E(e, { X = {1, 2, 3} } ) --> 14 20 | = rima.E(e, { X = {a, b, c} } ) --> a^2 + b^2 + c^2 21 | 22 | 23 | ## Flexible Data 24 | 25 | Rima understands your data in the structures that you want to use. 26 | 27 | x, X = rima.R"x, X" 28 | e = rima.sum{x=X}(x.weight + x.quantity) 29 | 30 | 31 | ## Separation of Models and Data 32 | 33 | -------------------------------------------------------------------------------- /docs/differentiation.txt: -------------------------------------------------------------------------------- 1 | --! env rima = require("rima") 2 | 3 | # Rima Manual: Differentiation 4 | 5 | [ [Contents](contents.html) | Previous: [Nonlinear Problems](nonlinear.html) ] 6 | 7 | Rima can symbolically differentiate an expression with respect to a variable: 8 | 9 | rima.define("x, y") 10 | print(rima.diff(1, x)) --> 0 11 | print(rima.diff(x, x)) --> 1 12 | print(rima.diff(y, x)) --> 0 13 | print(rima.diff(x * y, x)) --> y 14 | print(rima.diff(x^2, x)) --> 2*x 15 | 16 | or even: 17 | 18 | --! continue 19 | print(rima.diff((rima.sin(x))^(x^2), x)) 20 | --> (cos(x)/sin(x)*x^2 + 2*log(sin(x))*x)*sin(x)^(x^2) 21 | 22 | Rima uses automatic differentiation to generate gradients and hessians for 23 | nonlinear problems. 24 | 25 | [ [Contents](contents.html) | Previous: [Nonlinear Problems](nonlinear.html) ] 26 | 27 | -------------------------------------------------------------------------------- /lua/tests/rima/mp/constraint.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local number_t = require("rima.types.number_t") 5 | local interface = require("rima.interface") 6 | 7 | 8 | ------------------------------------------------------------------------------ 9 | 10 | return function(T) 11 | local E = interface.eval 12 | local R = interface.R 13 | 14 | local a, b, c, d = R"a, b, c, d" 15 | local S = { a = number_t.free(), b = 3, c = number_t.free(), d = 5 } 16 | local C 17 | T:expect_ok(function() C = interface.mp.constraint(a * b + c * d, "<=", 3) end) 18 | T:check_equal(C, "a*b + c*d <= 3") 19 | T:check_equal(E(C, S), "3*a + 5*c <= 3") 20 | 21 | local lower, upper, lhs, _ 22 | T:expect_ok(function() lower, upper, _, lhs = C:characterise(S) end) 23 | T:check_equal(upper, 3) 24 | T:check_equal(lower, -math.huge) 25 | T:check_equal(lhs.a.coeff, 3) 26 | T:check_equal(lhs.c.coeff, 5) 27 | end 28 | 29 | 30 | ------------------------------------------------------------------------------ 31 | -------------------------------------------------------------------------------- /lua/examples/nonlinear.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | require("rima") 5 | 6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | --[[ 10 | This simple nonlinear problem is copied from the ipopt documentation: 11 | http://www.coin-or.org/Ipopt/documentation/node28.html 12 | --]] 13 | 14 | rima.define"x, X" 15 | 16 | m = rima.mp.new{ 17 | sense = "minimise", 18 | objective = X[1]*X[4]*(X[1] + X[2] + X[3]) + X[3], 19 | c1 = rima.mp.C(rima.product{x=X}(x), ">=", 25), 20 | c2 = rima.mp.C(rima.sum{x=X}(x^2), "==", 40), 21 | X = { rima.free(1, 5), rima.free(1, 5), rima.free(1, 5), rima.free(1, 5) } 22 | } 23 | 24 | local primal, message = rima.mp.solve(m) 25 | if primal then 26 | io.stderr:write(("Nonlinear solution:\n objective: %g\n variables %g %g %g %g\n"):format( 27 | primal.objective, primal.X[1], primal.X[2], primal.X[3], primal.X[4])) 28 | else 29 | io.stderr:write(message, "\n") 30 | end 31 | 32 | 33 | -- EOF ------------------------------------------------------------------------- 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2011 Incremental IP Limited 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | 22 | -------------------------------------------------------------------------------- /lua/tests/rima/operators/product.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local product = require("rima.operators.product") 5 | 6 | local object = require("rima.lib.object") 7 | local lib = require("rima.lib") 8 | local scope = require("rima.scope") 9 | local number_t = require("rima.types.number_t") 10 | local interface = require("rima.interface") 11 | 12 | 13 | ------------------------------------------------------------------------------ 14 | 15 | return function(T) 16 | local E = interface.eval 17 | local D = lib.dump 18 | local R = interface.R 19 | 20 | T:test(object.typeinfo(product:new()).product, "typeinfo(product:new()).product") 21 | T:check_equal(object.typename(product:new()), "product", "typename(product:new()) == 'product'") 22 | 23 | local x, X = R"x, X" 24 | 25 | T:check_equal(E(interface.product{x=X}(x), {X={1, 2, 3, 4, 5}}), 120) 26 | T:check_equal(E(interface.product{x=X}(x), {X={number_t.free(), number_t.free(), number_t.free()}}), "X[1]*X[2]*X[3]") 27 | end 28 | 29 | 30 | ------------------------------------------------------------------------------ 31 | 32 | -------------------------------------------------------------------------------- /lua/rima/solvers/cbc.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local assert, ipairs, pcall = assert, ipairs, pcall 5 | 6 | local linear = require("rima.solvers.linear") 7 | 8 | local status, core = pcall(require, "rima_cbc_core") 9 | 10 | module(...) 11 | 12 | 13 | -------------------------------------------------------------------------------- 14 | 15 | available = status 16 | problem = not available and core 17 | objective = { linear = true } 18 | constraints = { linear = true } 19 | variables = { continuous = true, integer = true } 20 | 21 | preference = 1 22 | 23 | 24 | -------------------------------------------------------------------------------- 25 | 26 | local function solve_(options) 27 | linear.build_linear_problem(options) 28 | local m = core.new() 29 | assert(m:set_objective(options.ordered_variables, options.sense)) 30 | assert(m:build_rows(options.constraint_info)) 31 | assert(m:solve()) 32 | return assert(m:get_solution()) 33 | end 34 | 35 | solve = (status and solve_) or nil 36 | 37 | 38 | -- EOF ------------------------------------------------------------------------- 39 | 40 | -------------------------------------------------------------------------------- /docs/contents.txt: -------------------------------------------------------------------------------- 1 | # Rima Documentation 2 | 3 | ## Table of Contents 4 | 5 | - [Home Page](index.html) 6 | - [Quick Look at a Model](knapsack.html) 7 | - [Obtaining and Installing Rima](install.html) 8 | - Rima Manual 9 | --! ignore 10 | - [Enough Lua to get you in Trouble](trouble.html) 11 | - [References](references.html) 12 | - [Expressions](expressions.html) 13 | - [A Simple LP](simple_lp.html) 14 | - [Arrays](arrays.html) 15 | - [Sums](sums.html) 16 | - [A Blending Problem](blending.html) 17 | - [Structures](structures.html) 18 | - [A Structured Knapsack](knapsack_2.html) 19 | - [Functions](functions.html) 20 | - [Cases](cases.html) 21 | - [Nonlinear Problems](nonlinear.html) 22 | - [Differentiation](differentiation.html) 23 | - Examples (Lua code, not html) 24 | --! ignore 25 | - [Simple Example](examples/simple.lua) 26 | - [Knapsack](examples/knapsack.lua) 27 | - [Blending](examples/whiskas.lua) 28 | - [Curve Fitting](examples/minimax.lua) 29 | - [Assignment and Facility Location](examples/assignment.lua) 30 | - [Sudoku](examples/sudoku.lua) 31 | - [Nonlinear](examples/nonlinear.lua) 32 | 33 | -------------------------------------------------------------------------------- /lua/tests/rima/operators/mod.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local mod = require("rima.operators.mod") 5 | 6 | local object = require("rima.lib.object") 7 | local lib = require("rima.lib") 8 | local interface = require("rima.interface") 9 | 10 | 11 | ------------------------------------------------------------------------------ 12 | 13 | return function(T) 14 | local E = interface.eval 15 | local D = lib.dump 16 | local R = interface.R 17 | 18 | T:test(object.typeinfo(mod:new()).mod, "typeinfo(mod:new()).mod") 19 | T:check_equal(object.typename(mod:new()), "mod", "typename(mod:new()) == 'mod'") 20 | 21 | local a, b = R"a, b" 22 | local S = { a = 5 } 23 | 24 | T:check_equal(D(a%2), 'expression(%(index(address{"a"}), 2))') 25 | T:check_equal(a%2, "a%2") 26 | T:check_equal(a%b, "a%b") 27 | T:check_equal(E(a%2, S), 1) 28 | T:check_equal(D(2%a), 'expression(%(2, index(address{"a"})))') 29 | T:check_equal(E(7%a, S), 2) 30 | 31 | -- Identities 32 | T:check_equal(E(0%b, S), 0) 33 | end 34 | 35 | 36 | ------------------------------------------------------------------------------ 37 | 38 | -------------------------------------------------------------------------------- /lua/rima/solvers/clp.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local assert, ipairs, pcall = assert, ipairs, pcall 5 | 6 | local linear = require("rima.solvers.linear") 7 | 8 | local status, core = pcall(require, "rima_clp_core") 9 | 10 | module(...) 11 | 12 | 13 | -------------------------------------------------------------------------------- 14 | 15 | available = status 16 | problem = not available and core 17 | objective = { linear = true } 18 | constraints = { linear = true } 19 | variables = { continuous = true } 20 | 21 | preference = 0 22 | 23 | 24 | -------------------------------------------------------------------------------- 25 | 26 | local function solve_(options) 27 | linear.build_linear_problem(options) 28 | local m = core.new() 29 | assert(m:resize(0, #options.ordered_variables)) 30 | assert(m:build_rows(options.sparse_constraints)) 31 | assert(m:set_objective(options.ordered_variables, options.sense)) 32 | assert(m:solve()) 33 | return assert(m:get_solution()) 34 | end 35 | 36 | solve = (status and solve_) or nil 37 | 38 | 39 | -- EOF ------------------------------------------------------------------------- 40 | 41 | -------------------------------------------------------------------------------- /lua/examples/simple.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | require("rima") 5 | 6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | --[[ 10 | This simple problem is copied from an OSI example: 11 | https://projects.coin-or.org/svn/Osi/trunk/Osi/examples/build.cpp 12 | --]] 13 | 14 | io.write("\nSimple Test:\n") 15 | 16 | rima.define"x, y" 17 | S = rima.mp.new() 18 | S.c1 = rima.mp.C(x + 2*y, "<=", 3) 19 | S.c2 = rima.mp.C(2*x + y, "<=", 3) 20 | S.objective = x + y 21 | S.sense = "maximise" 22 | S.x = rima.positive() 23 | S.y = rima.positive() 24 | 25 | io.write("Algebraic Form:\n") 26 | io.write(tostring(S)) 27 | 28 | io.write("Solutions:\n") 29 | local function s(solver) 30 | local primal, dual = rima.mp.solve_with(solver, S) 31 | if primal then 32 | io.write(("\n%s:\n objective: \t% 10.2f\n variables and constraints:\n"):format(solver, primal.objective)) 33 | for k, v in pairs(dual) do io.write((" %-10s\t% 10.2f\t(% 10.2f)\n"):format(k, primal[k], v)) end 34 | end 35 | end 36 | 37 | s("lpsolve") 38 | s("clp") 39 | s("cbc") 40 | 41 | 42 | -- EOF ------------------------------------------------------------------------- 43 | 44 | -------------------------------------------------------------------------------- /lua/tests/rima/core.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local core = require("rima.core") 5 | 6 | local lib = require("rima.lib") 7 | 8 | 9 | ------------------------------------------------------------------------------ 10 | 11 | return function(T) 12 | local E = core.eval 13 | local D = lib.dump 14 | local DE = function(...) return D(E(...)) end 15 | 16 | -- literals -------------------------- 17 | -- evaluating 18 | T:check_equal(DE(1), 1) 19 | T:check_equal(DE("a"), '"a"') 20 | T:check_equal(DE(nil), "nil") 21 | 22 | -- defined 23 | T:check_equal(core.defined(1), true) 24 | T:check_equal(core.defined("a"), true) 25 | T:check_equal(core.defined(nil), true) 26 | 27 | -- objects --------------------------- 28 | do 29 | local o1 = setmetatable({}, { __eval = function(e) return 17 end }) 30 | T:check_equal(DE(o1), 17) 31 | T:check_equal(core.defined(o1), false) 32 | 33 | local o2 = setmetatable({}, { __eval = function(e) return 17 end , __defined = function(e) return true end }) 34 | T:check_equal(core.defined(o2), true) 35 | end 36 | 37 | end 38 | 39 | 40 | ------------------------------------------------------------------------------ 41 | 42 | -------------------------------------------------------------------------------- /lua/rima/solvers/lpsolve.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local assert, ipairs, pcall = assert, ipairs, pcall 5 | 6 | local linear = require("rima.solvers.linear") 7 | 8 | local status, core = pcall(require, "rima_lpsolve_core") 9 | 10 | module(...) 11 | 12 | 13 | -------------------------------------------------------------------------------- 14 | 15 | available = status 16 | problem = not available and core 17 | objective = { linear = true } 18 | constraints = { linear = true } 19 | variables = { continuous = true, integer = true } 20 | 21 | preference = 2 22 | 23 | 24 | -------------------------------------------------------------------------------- 25 | 26 | local function solve_(options) 27 | linear.build_linear_problem(options) 28 | local m = core.new(0, #options.ordered_variables) 29 | assert(m:resize(#options.constraint_info, #options.ordered_variables)) 30 | assert(m:build_rows(options.constraint_info)) 31 | assert(m:set_objective(options.ordered_variables, options.sense)) 32 | assert(m:solve()) 33 | return assert(m:get_solution()) 34 | end 35 | 36 | solve = (status and solve_) or nil 37 | 38 | 39 | -- EOF ------------------------------------------------------------------------- 40 | 41 | -------------------------------------------------------------------------------- /lua/tests/rima/sets/element.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local element = require("rima.sets.element") 5 | 6 | local object = require("rima.lib.object") 7 | local lib = require("rima.lib") 8 | local core = require("rima.core") 9 | local interface = require("rima.interface") 10 | 11 | 12 | ------------------------------------------------------------------------------ 13 | 14 | return function(T) 15 | local function N(...) return element:new(...) end 16 | local E = interface.eval 17 | local D = lib.dump 18 | 19 | -- Constructors 20 | T:test(object.typeinfo(N()).element, "typeinfo(element:new()).element") 21 | T:check_equal(object.typename(N()), "element", "typename(element:new()) == 'element'") 22 | 23 | do 24 | local it 25 | T:expect_ok(function() it = N(nil, "key", 13) end) 26 | T:check_equal(it + 17, 30) 27 | T:check_equal(7 * it, 91) 28 | T:check_equal(core.defined(it), true) 29 | end 30 | 31 | do 32 | local a = interface.R"a" 33 | local S = { a = N(nil, "key", 13) } 34 | T:check_equal(E(a + 19, S), 32) 35 | T:check_equal(D(E(a + 19, S)), 32) 36 | end 37 | end 38 | 39 | 40 | ------------------------------------------------------------------------------ 41 | 42 | -------------------------------------------------------------------------------- /lua/tests/rima/operators/add.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local ops = require("rima.operations") 5 | 6 | local object = require("rima.lib.object") 7 | local lib = require("rima.lib") 8 | local expression = require("rima.expression") 9 | local index = require("rima.index") 10 | 11 | ------------------------------------------------------------------------------ 12 | 13 | return function(T) 14 | local A = ops.add(1, index:new(nil, "a")) 15 | local W = expression.wrap 16 | local U = expression.unwrap 17 | 18 | -- constructors 19 | T:test(object.typeinfo(A).add, "typeinfo(add).add") 20 | T:check_equal(object.typename(A), "add", "typename(add)=='add'") 21 | 22 | local A2 = ops.add(5, "a") 23 | T:test(object.typeinfo(A2).add, "typeinfo(add).add") 24 | T:check_equal(object.typename(A2), "add", "typename(add)=='add'") 25 | 26 | local A3 = 1 + W(A) 27 | T:test(object.typeinfo(U(A3)).add, "typeinfo(add).add") 28 | T:check_equal(object.typename(U(A3)), "add", "typename(add)=='add'") 29 | T:test(lib.getmetamethod(A3, "__add")) 30 | 31 | -- string representation 32 | T:check_equal(A, "1 + a") 33 | T:check_equal(A2, "5 + a") 34 | end 35 | 36 | 37 | ------------------------------------------------------------------------------ 38 | 39 | -------------------------------------------------------------------------------- /lua/tests/rima/operators/pow.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local pow = require("rima.operators.pow") 5 | 6 | local object = require("rima.lib.object") 7 | local lib = require("rima.lib") 8 | local interface = require("rima.interface") 9 | 10 | 11 | ------------------------------------------------------------------------------ 12 | 13 | return function(T) 14 | local E = interface.eval 15 | local D = lib.dump 16 | local R = interface.R 17 | 18 | T:test(object.typeinfo(pow:new()).pow, "typeinfo(pow:new()).pow") 19 | T:check_equal(object.typename(pow:new()), "pow", "typename(pow:new()) == 'pow'") 20 | 21 | local a, b = R"a, b" 22 | local S = { a = 5 } 23 | 24 | T:check_equal(D(a^2), 'expression(*(index(address{"a"})^2))') 25 | T:check_equal(a^2, "a^2") 26 | T:check_equal(a^b, "a^b") 27 | T:check_equal(E(a^2, S), 25) 28 | T:check_equal(D(2^a), 'expression(^(2, index(address{"a"})))') 29 | T:check_equal(E(2^a, S), 32) 30 | 31 | -- Identities 32 | T:check_equal(E(0^b, S), 0) 33 | T:check_equal(E(1^b, S), 1) 34 | T:check_equal(E(b^0, S), 1) 35 | T:check_equal(D(E(b^1, S)), 'expression(index(address{"b"}))') 36 | 37 | -- Tests including add and mul are in rima.expression 38 | end 39 | 40 | 41 | ------------------------------------------------------------------------------ 42 | 43 | -------------------------------------------------------------------------------- /lua/tests/rima/lib/object.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local object = require("rima.lib.object") 5 | 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | return function(T) 10 | local o = object:new() 11 | T:test(object.typeinfo(o).object, "typeinfo(o).object") 12 | T:check_equal(object.typename(o), "object", "typename(object) == 'object'") 13 | 14 | local subobj = object:new_class(nil, "subobj") 15 | T:check_equal(object.typename(subobj), "object", "typename(subobj) == 'object'") 16 | local s = subobj:new() 17 | T:check_equal(object.typename(s), "subobj", "typename(s) == 'subobj'") 18 | T:test(object.typeinfo(s).object, "typeinfo(s).object") 19 | T:test(object.typeinfo(s).subobj, "typeinfo(s).subobj") 20 | T:test(not object.typeinfo(s).table, "not typeinfo(s).table") 21 | 22 | T:test(not object.typeinfo({}).object, "not typeinfo({}).object") 23 | T:test(not object.typeinfo({}).subobj, "not typeinfo({}).suboj") 24 | T:test(not object.typeinfo(o).subobj, "not typeinfo(object:new()).subobj") 25 | 26 | T:check_equal(object.typename(s), "subobj", "typename(s) == 'subobj'") 27 | T:check_equal(object.typename(1), "number", "typename(1) == 'number'") 28 | end 29 | 30 | 31 | ------------------------------------------------------------------------------ 32 | 33 | -------------------------------------------------------------------------------- /lua/tests/rima/operators/case.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local case = require("rima.operators.case") 5 | 6 | local object = require("rima.lib.object") 7 | local lib = require("rima.lib") 8 | local interface = require("rima.interface") 9 | 10 | 11 | ------------------------------------------------------------------------------ 12 | 13 | return function(T) 14 | local E = interface.eval 15 | local D = lib.dump 16 | local R = interface.R 17 | local U = interface.unwrap 18 | 19 | T:test(object.typeinfo(U(interface.case(1, {{1, 1}}))).case, "typeinfo(case).case") 20 | T:check_equal(object.typename(U(interface.case(1, {{1, 1}}))), "case", "typename(case) == 'case'") 21 | 22 | do 23 | local a, b, c, d, e, f, g, h = R"a, b, c, d, e, f, g, h" 24 | local C = interface.case(a, {{b, c},{d, e},{f, g}}, h) 25 | 26 | T:check_equal(C, "case a (b: c; d: e; f: g; default: h; )") 27 | T:check_equal(E(C, {a = 1, f = 1}), "case 1 (b: c; d: e; 1: g; )") 28 | T:check_equal(E(C, {a = 1, d = 1}), "case 1 (b: c; 1: e; )") 29 | T:check_equal(E(C, {a = 1, f = 2}), "case 1 (b: c; d: e; default: h; )") 30 | T:check_equal(E(C, {a = 1, b = 3, d = 1}), "e") 31 | T:check_equal(E(C, {a = 1, b = 3, d = 3, f = 3}), "h") 32 | end 33 | end 34 | 35 | 36 | ------------------------------------------------------------------------------ 37 | 38 | -------------------------------------------------------------------------------- /c/rima_solver_tools.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | 3 | rima_solver_tools.h 4 | 5 | Copyright (c) 2009-2011 Incremental IP Limited 6 | see LICENSE for license information 7 | 8 | *******************************************************************************/ 9 | 10 | #ifndef rima_solver_tools_h 11 | #define rima_solver_tools_h 12 | 13 | extern "C" 14 | { 15 | #include "lualib.h" 16 | } 17 | 18 | /*============================================================================*/ 19 | 20 | int error(lua_State *L, const char *s); 21 | 22 | const char *check_constraints(lua_State *L, unsigned constraint_count, unsigned column_count, unsigned &max_non_zeroes); 23 | 24 | typedef const char *(constraint_builder_function)(void *data, unsigned non_zeroes, int *columns, double *coefficients, double lower, double upper); 25 | const char *build_constraints(lua_State *L, unsigned max_non_zeroes, unsigned constraint_count, int column_offset, constraint_builder_function *bf, void *bfd); 26 | 27 | const char *check_variables(lua_State *L, unsigned variable_count); 28 | typedef const char *(variable_builder_function)(void *data, unsigned index, double cost, double lower, double upper, bool integer); 29 | const char *build_variables(lua_State *L, unsigned variable_count, variable_builder_function *bf, void *bfd); 30 | 31 | /*============================================================================*/ 32 | #endif 33 | 34 | -------------------------------------------------------------------------------- /docs/cases.txt: -------------------------------------------------------------------------------- 1 | --! env rima = require("rima") 2 | 3 | # Rima Manual: Cases 4 | 5 | [ [Contents](contents.html) | Previous: [Functions](functions.html) ] | Next: [Nonlinear Problems](nonlinear.html) ] 6 | 7 | A `case` is Rima's generalisation of an if statement. 8 | The syntax isn't pretty, so you can make any suggestions you like: 9 | 10 | value, m1, r1, m2, r2, default = rima.R"value, m1, r1, m2, r2, default" 11 | c = rima.case(value, {{m1, r1}, {m2, r2}}, default) 12 | print(c) --> case value (m1: r1; m2: r2; default: default; ) 13 | print(rima.E(c, {value=1, m1=1})) --> r1 14 | 15 | If there's more than one match, the case always picks the first: 16 | 17 | --! continue 18 | print(rima.E(c, {value=1, m1=1, m2=1})) --> r1 19 | 20 | If an early match isn't yet defined, then the case will remain undefined, even if there's a later match 21 | (but following matches and the default will be discarded) 22 | 23 | --! continue 24 | print(rima.E(c, {value=1, m2=1})) --> case 1 (m1: r1; 1: r2; ) 25 | 26 | If there's a non-match, but no actual match, the non-match is discarded: 27 | 28 | --! continue 29 | print(rima.E(c, {value=1, m1=2})) --> case 1 (m2: r2; default: default; ) 30 | 31 | 32 | [ [Contents](contents.html) | Previous: [Functions](functions.html) ] | Next: [Nonlinear Problems](nonlinear.html) ] 33 | 34 | -------------------------------------------------------------------------------- /lua/rima-test.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local trace = require("rima.lib.trace") 5 | 6 | -------------------------------------------------------------------------------- 7 | 8 | local strict = true 9 | local patterns = {} 10 | local options = {} 11 | 12 | for _, v in ipairs{...} do 13 | if v == "--show-passes" then 14 | options.show_passes = true 15 | elseif v == "--quiet" then 16 | options.quiet = true 17 | elseif v == "--dont-show-fails" then 18 | options.dont_show_fails = true 19 | elseif v == "--no-strict" then 20 | strict = false 21 | elseif v == "--tron" then 22 | trace.tron() 23 | else 24 | patterns[#patterns+1] = v 25 | end 26 | end 27 | 28 | local directory = require("test.directory") 29 | local expressions = require("test.expression_files") 30 | 31 | if strict then 32 | ok, message = pcall(require, "test.strict") 33 | if not ok then 34 | io.stderr:write("warning: strict not available\n") 35 | end 36 | end 37 | 38 | local p1, t1, f1 = directory.test("tests", options, patterns) 39 | local p2, t2, f2 = expressions.test("../expressions") 40 | 41 | local passed = p1 and p2 42 | local tests = t1 + t2 43 | local fails = f1 + f2 44 | 45 | if passed then 46 | io.stderr:write("All tests completed successfully\n") 47 | os.exit(0) 48 | else 49 | io.stderr:write(("Failed %d/%d tests\n"):format(fails, tests)) 50 | os.exit(fails) 51 | end 52 | 53 | 54 | -- EOF ------------------------------------------------------------------------- 55 | -------------------------------------------------------------------------------- /docs/nonlinear.txt: -------------------------------------------------------------------------------- 1 | --! env rima = require("rima") 2 | 3 | # Rima Manual: Nonlinear Problems 4 | 5 | [ [Contents](contents.html) | Previous: [Cases](cases.html) | Next: [Differentiation](differentiation.html) ] 6 | 7 | Rima can be used to formulate nonlinear problems just like nonlinear problems, 8 | and will use [ipopt](https://projects.coin-or.org/Ipopt) to solve them if it's 9 | available. 10 | 11 | rima.define"x, X" 12 | nonlinear = rima.mp.new{ \ 13 | sense = "minimise", \ 14 | objective = X[1]*X[4]*(X[1] + X[2] + X[3]) + X[3], \ 15 | c1 = rima.mp.C(rima.product{x=X}(x), ">=", 25), \ 16 | c2 = rima.mp.C(rima.sum{x=X}(x^2), "==", 40), \ 17 | X = { rima.free(1, 5), rima.free(1, 5), rima.free(1, 5), rima.free(1, 5) } \ 18 | } 19 | 20 | (This example is taken from the [ipopt documentation](http://www.coin-or.org/Ipopt/documentation/node28.html). 21 | As usual, you can print the model: 22 | 23 | --! continue 24 | print(nonlinear) 25 | --> Minimise: 26 | --> X[1]*(X[1] + X[2] + X[3])*X[4] + X[3] 27 | --> Subject to: 28 | --> c1: X[1]*X[2]*X[3]*X[4] >= 25 29 | --> c2: X[1]^2 + X[2]^2 + X[3]^2 + X[4]^2 == 40 30 | 31 | `rima.mp.solve` will inspect the problem, work out that it's nonlinear and use 32 | ipopt. 33 | Keep in mind that when you solve, you don't get duals: 34 | 35 | --! continue 36 | primal = rima.mp.solve(nonlinear) 37 | print(primal.objective) --> /17.0141*/ 17.014 38 | 39 | [ [Contents](contents.html) | Previous: [Cases](cases.html) | Next: [Differentiation](differentiation.html) ] 40 | -------------------------------------------------------------------------------- /lua/rima/operators/minmax.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local operator = require("rima.operator") 5 | local core = require("rima.core") 6 | 7 | 8 | ------------------------------------------------------------------------------ 9 | 10 | min = operator:new_class({}, "min") 11 | min.precedence = 0 12 | 13 | function min:__eval(...) 14 | local a2 = {} 15 | local m = math.huge 16 | for _, a in ipairs(self) do 17 | a = core.eval(a, ...) 18 | if type(a) == "number" then 19 | if a < m then 20 | m = a 21 | end 22 | else 23 | a2[#a2+1] = a 24 | end 25 | end 26 | 27 | if a2[1] then 28 | a2[#a2+1] = m 29 | return min:new(a2) 30 | else 31 | return m 32 | end 33 | end 34 | 35 | 36 | ------------------------------------------------------------------------------ 37 | 38 | max = operator:new_class({}, "max") 39 | max.precedence = 0 40 | 41 | function max:__eval(...) 42 | local a2 = {} 43 | local m = -math.huge 44 | for _, a in ipairs(self) do 45 | a = core.eval(a, ...) 46 | if type(a) == "number" then 47 | if a > m then 48 | m = a 49 | end 50 | else 51 | a2[#a2+1] = a 52 | end 53 | end 54 | 55 | if a2[1] then 56 | a2[#a2+1] = m 57 | return max:new(a2) 58 | else 59 | return m 60 | end 61 | end 62 | 63 | 64 | ------------------------------------------------------------------------------ 65 | 66 | return { min = min, max = max } 67 | 68 | ------------------------------------------------------------------------------ 69 | 70 | -------------------------------------------------------------------------------- /lua/tests/rima/operators/minmax.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local minmax = require("rima.operators.minmax") 5 | 6 | local object = require("rima.lib.object") 7 | local lib = require("rima.lib") 8 | local interface = require("rima.interface") 9 | 10 | 11 | ------------------------------------------------------------------------------ 12 | 13 | return function(T) 14 | local E = interface.eval 15 | local D = lib.dump 16 | local R = interface.R 17 | local U = interface.unwrap 18 | 19 | -- min 20 | T:test(object.typeinfo(U(interface.min(1, {1, 1}))).min, "typeinfo(min).min") 21 | T:check_equal(object.typename(U(interface.min(1, {1, 1}))), "min", "typename(min) == 'min'") 22 | 23 | do 24 | local a, b, c, d = R"a, b, c, d" 25 | local C = interface.min(a, b, c, d) 26 | 27 | T:check_equal(C, "min(a, b, c, d)") 28 | T:check_equal(E(C, {a = 1}), "min(b, c, d, 1)") 29 | T:check_equal(E(C, {a = 1, b = 2}), "min(c, d, 1)") 30 | T:check_equal(E(C, {a = 1, b = 2, c = 3, d = 4}), 1) 31 | end 32 | 33 | -- max 34 | T:test(object.typeinfo(U(interface.max(1, {1, 1}))).max, "typeinfo(max).max") 35 | T:check_equal(object.typename(U(interface.max(1, {1, 1}))), "max", "typename(max) == 'max'") 36 | 37 | do 38 | local a, b, c, d = R"a, b, c, d" 39 | local C = interface.max(a, b, c, d) 40 | 41 | T:check_equal(C, "max(a, b, c, d)") 42 | T:check_equal(E(C, {a = 1}), "max(b, c, d, 1)") 43 | T:check_equal(E(C, {a = 1, b = 2}), "max(c, d, 2)") 44 | T:check_equal(E(C, {a = 1, b = 2, c = 3, d = 4}), 4) 45 | end 46 | end 47 | 48 | 49 | ------------------------------------------------------------------------------ 50 | 51 | -------------------------------------------------------------------------------- /lua/rima/operations.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2013 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | ------------------------------------------------------------------------------ 5 | -- The operators are lazy loaded to avoid a require conflict between 6 | -- the operators themselves (some of which use this module) and this module 7 | -- (which requires the operators). 8 | -- It would be nice if there was a better workaround. 9 | 10 | local op_modules = setmetatable({}, 11 | { 12 | __index = function(t, k) 13 | t[k] = require("rima.operators."..k) 14 | return t[k] 15 | end 16 | }) 17 | 18 | 19 | ------------------------------------------------------------------------------ 20 | 21 | local ops = {} 22 | 23 | 24 | function ops.add(...) 25 | local t = {} 26 | for i = 1, select("#", ...) do 27 | t[i] = { 1, (select(i, ...)) } 28 | end 29 | return op_modules.add:new(t) 30 | end 31 | 32 | 33 | function ops.sub(a, b) 34 | return op_modules.add:new{{ 1, a }, { -1, b }} 35 | end 36 | 37 | 38 | function ops.unm(a) 39 | return op_modules.add:new{{ -1, a }} 40 | end 41 | 42 | 43 | function ops.mul(...) 44 | local t = {} 45 | for i = 1, select("#", ...) do 46 | t[i] = { 1, (select(i, ...)) } 47 | end 48 | return op_modules.mul:new(t) 49 | end 50 | 51 | 52 | function ops.div(a, b) 53 | return op_modules.mul:new{{ 1, a }, { -1, b }} 54 | end 55 | 56 | 57 | function ops.pow(a, b) 58 | return op_modules.pow:new{ a, b } 59 | end 60 | 61 | 62 | function ops.mod(a, b) 63 | return op_modules.mod:new{ a, b } 64 | end 65 | 66 | 67 | ------------------------------------------------------------------------------ 68 | 69 | return ops 70 | 71 | ------------------------------------------------------------------------------ 72 | 73 | -------------------------------------------------------------------------------- /lua/rima/lib/trace.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local debug, io = require("debug"), require("io") 5 | local select = select 6 | 7 | local lib = require("rima.lib") 8 | 9 | module(...) 10 | 11 | 12 | -- Evaluation tracing ---------------------------------------------------------- 13 | 14 | _M.on = false 15 | _M.depth = 0 16 | 17 | function tron() 18 | on = true 19 | end 20 | 21 | 22 | function troff() 23 | on = false 24 | end 25 | 26 | 27 | function reset_depth() 28 | depth = 0 29 | end 30 | 31 | 32 | function enter(tag, caller_depth, callee, ...) 33 | local caller = debug.getinfo(caller_depth and caller_depth+2 or 3, "Sln") 34 | callee = callee and debug.getinfo(callee, "Sln") 35 | caller = ("from %s:%d"):format(caller.short_src, caller.currentline) 36 | callee = (callee and (" to %s:%d"):format(callee.short_src, callee.linedefined)) or "" 37 | io.write(("%s%s>: "):format(("| "):rep(depth), tag)) 38 | for i = 1, select('#', ...) do 39 | local a = select(i, ...) 40 | io.write(("%s: %s "):format(lib.repr(a), lib.dump(a))) 41 | end 42 | io.write(("(%s%s)\n"):format(caller, callee)) 43 | depth = depth + 1 44 | end 45 | 46 | 47 | function leave(tag, arg_count, ...) 48 | depth = depth - 1 49 | io.write(("%s%s<: "):format(("| "):rep(depth), tag)) 50 | for i = 1, arg_count do 51 | local a = select(i, ...) 52 | io.write(("%s "):format(lib.repr(a))) 53 | end 54 | io.write("= ") 55 | for i = arg_count + 1, select('#', ...) do 56 | local a = select(i, ...) 57 | if a then 58 | io.write(("%s: %s "):format(lib.repr(a), lib.dump(a))) 59 | end 60 | end 61 | io.write("\n") 62 | end 63 | 64 | 65 | -- EOF ------------------------------------------------------------------------- 66 | 67 | -------------------------------------------------------------------------------- /lua/test/directory.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local lfs = require("lfs") 5 | local series = require("test.series") 6 | 7 | 8 | ------------------------------------------------------------------------------ 9 | 10 | local function run_one(T, path, options, patterns) 11 | path = path:gsub("/", "."):gsub("%.lua$", "") 12 | 13 | local run = true 14 | if patterns and patterns[1] then 15 | if patterns[1]:sub(1,1) ~= "-" then 16 | run = false 17 | end 18 | for _, p in ipairs(patterns) do 19 | if p:sub(1,1) == "-" then 20 | if path:match(p:sub(2)) then 21 | run = false 22 | end 23 | elseif path:match(p) then 24 | run = true 25 | end 26 | end 27 | end 28 | 29 | if run then 30 | local test = require(path) 31 | T:run(test, path) 32 | end 33 | end 34 | 35 | 36 | local function _test(path, options, patterns, T) 37 | for f in lfs.dir(path) do 38 | if not f:match("^%.") then 39 | f = path.."/"..f 40 | local mode = lfs.attributes(f, "mode") 41 | if mode == "directory" then 42 | T:run(function(T) 43 | return _test(f, options, patterns, T) 44 | end, f:gsub("/", ".")) 45 | elseif f:match("%.lua$") then 46 | run_one(T, f, options, patterns) 47 | end 48 | end 49 | end 50 | end 51 | 52 | 53 | local function test(path, options, patterns) 54 | local T = series:new(options, path:gsub("/", ".")) 55 | _test(path, options, patterns, T) 56 | return T:close() 57 | end 58 | 59 | 60 | ------------------------------------------------------------------------------ 61 | 62 | return { test = test } 63 | 64 | 65 | ------------------------------------------------------------------------------ 66 | 67 | -------------------------------------------------------------------------------- /lua/tests/rima/differentiation.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local lib = require("rima.lib") 5 | local interface = require("rima.interface") 6 | 7 | 8 | ------------------------------------------------------------------------------ 9 | 10 | return function(T) 11 | local x = interface.R"x" 12 | local opmath = interface.math 13 | local diff = interface.diff 14 | 15 | T:check_equal(diff(1, x), 0) 16 | T:check_equal(diff(x, x), 1) 17 | T:check_equal(diff(x*x, x), "2*x") 18 | T:check_equal(diff(x*x*x, x), "3*x^2") 19 | T:check_equal(diff(x^2, x), "2*x") 20 | T:check_equal(diff(x^3, x), "3*x^2") 21 | T:check_equal(diff(x^0.5, x), "0.5/x^0.5") 22 | T:check_equal(diff(2^x, x), "0.6931*2^x") 23 | T:check_equal(diff(10^x, x), "2.303*10^x") 24 | T:check_equal(diff(x^x, x), "(1 + log(x))*x^x") 25 | T:check_equal(diff(opmath.exp(x), x), "exp(x)") 26 | T:check_equal(diff(opmath.exp(2*x), x), "2*exp(2*x)") 27 | T:check_equal(diff(opmath.exp(x*x), x), "2*exp(x^2)*x") 28 | T:check_equal(diff(opmath.exp(x^2), x), "2*exp(x^2)*x") 29 | T:check_equal(diff(opmath.log(x), x), "1/x") 30 | T:check_equal(diff(opmath.log(2*x), x), "1/x") 31 | T:check_equal(diff(opmath.log(x*x), x), "2/x") 32 | T:check_equal(diff(opmath.log(x^2), x), "2/x") 33 | T:check_equal(diff(opmath.sin(x), x), "cos(x)") 34 | T:check_equal(diff(opmath.sin(2*x), x), "2*cos(2*x)") 35 | T:check_equal(diff(opmath.sin(x*x), x), "2*cos(x^2)*x") 36 | T:check_equal(diff(opmath.sin(x^2), x), "2*cos(x^2)*x") 37 | T:check_equal(diff(opmath.cos(x), x), "-1*sin(x)") 38 | 39 | T:check_equal(diff((opmath.sin(x))^(x^2), x), "(cos(x)/sin(x)*x^2 + 2*log(sin(x))*x)*sin(x)^(x^2)") 40 | end 41 | 42 | 43 | ------------------------------------------------------------------------------ 44 | 45 | -------------------------------------------------------------------------------- /lua/rima/solvers/linear.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local table = require("table") 5 | local pairs = pairs 6 | 7 | module(...) 8 | 9 | 10 | -------------------------------------------------------------------------------- 11 | 12 | function build_linear_problem(M) 13 | -- add costs to variables 14 | for name, v in pairs(M.variable_map) do 15 | local o = M.linear_objective[name] 16 | v.cost = (o and o.coeff) or 0 17 | end 18 | 19 | -- Build a set of sparse constraints 20 | local sparse_constraints = {} 21 | local i = 1 22 | for _, c in pairs(M.constraint_info) do 23 | local elements = {} 24 | local j = 1 25 | for name, element in pairs(c.linear_exp) do 26 | element.index = M.variable_map[name].index 27 | elements[j] = element 28 | j = j + 1 29 | end 30 | table.sort(elements, function(a, b) return a.index < b.index end) 31 | c.elements = elements 32 | sparse_constraints[i] = c 33 | i = i + 1 34 | end 35 | 36 | M.sparse_constraints = sparse_constraints 37 | end 38 | 39 | 40 | function write_sparse(M, f) 41 | f = f or io.stdout 42 | 43 | f:write("Minimise:\n") 44 | for i, v in ipairs(M.ordered_variables) do 45 | f:write((" %0.4g*%s (index=%d, lower=%0.4g, upper=%0.4g)\n"):format(v.cost, v.name, i, v.type.lower, v.type.upper)) 46 | end 47 | 48 | f:write("Subject to:\n") 49 | 50 | for _, c in ipairs(M.constraint_info) do 51 | f:write((" %0.4g <= "):format(c.lower)) 52 | for _, cc in ipairs(c.elements) do 53 | f:write(("%+0.4g*%s "):format(cc.coeff, M.ordered_variables[cc.index].name)) 54 | end 55 | f:write(("<= %0.4g\n"):format(c.upper)) 56 | end 57 | end 58 | 59 | 60 | -- EOF ------------------------------------------------------------------------- 61 | 62 | -------------------------------------------------------------------------------- /lua/rima/operator.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2013 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local object = require"rima.lib.object" 5 | local lib = require"rima.lib" 6 | local core = require"rima.core" 7 | 8 | 9 | ------------------------------------------------------------------------------ 10 | 11 | local operator = object:new_class({}, "operator") 12 | local ops = {} 13 | 14 | function operator:new_class(...) 15 | local op = object.new_class(self, ...) 16 | op.__tostring = lib.__tostring 17 | return op 18 | end 19 | 20 | 21 | ------------------------------------------------------------------------------ 22 | 23 | function operator:new(t) 24 | t = object.new(self, t) 25 | if self.simplify then t = self.simplify(t) end 26 | return t 27 | end 28 | 29 | 30 | ------------------------------------------------------------------------------ 31 | 32 | function operator.__repr(self, format) 33 | return object.typename(self).."("..lib.concat_repr(self, format)..")" 34 | end 35 | 36 | function operator:__list_variables(S, list) 37 | for i = 1, #self do 38 | core.list_variables(self[i], S, list) 39 | end 40 | end 41 | 42 | 43 | ------------------------------------------------------------------------------ 44 | 45 | function operator:evaluate_terms(...) 46 | local new_terms 47 | local term_count = #self 48 | for i = 1, term_count do 49 | local e = core.eval(self[i], ...) 50 | if e ~= self[i] then 51 | new_terms = new_terms or {} 52 | new_terms[i] = e 53 | end 54 | end 55 | if new_terms then 56 | for i = 1, term_count do 57 | new_terms[i] = new_terms[i] or self[i] 58 | end 59 | end 60 | 61 | return new_terms 62 | end 63 | 64 | 65 | ------------------------------------------------------------------------------ 66 | 67 | return operator 68 | 69 | ------------------------------------------------------------------------------ 70 | 71 | -------------------------------------------------------------------------------- /lua/rima/operators/mod.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local operator = require("rima.operator") 5 | local lib = require("rima.lib") 6 | local core = require("rima.core") 7 | 8 | 9 | ------------------------------------------------------------------------------ 10 | 11 | local mod = operator:new_class({}, "mod") 12 | mod.precedence = 3 13 | 14 | 15 | ------------------------------------------------------------------------------ 16 | 17 | function mod:__repr(format) 18 | local numerator, denominator = self[1], self[2] 19 | local repr = lib.repr 20 | local paren = core.parenthise 21 | local prec = mod.precedence 22 | 23 | local ff = format.format 24 | if ff == "dump" then 25 | return "%("..repr(numerator, format)..", "..repr(denominator, format)..")" 26 | elseif ff == "latex" then 27 | return "{"..paren(numerator, format, prec).."}\\bmod{"..paren(denominator, format, prec).."}" 28 | else 29 | return paren(numerator, format, prec).."%"..paren(denominator, format, prec) 30 | end 31 | end 32 | 33 | 34 | ------------------------------------------------------------------------------ 35 | 36 | function mod:simplify() 37 | if self[1] == 0 then 38 | return 0 39 | else 40 | return self 41 | end 42 | end 43 | 44 | 45 | function mod:__eval(...) 46 | local t1, t2 = self[1], self[2] 47 | local numerator, denominator = core.eval(t1, ...), core.eval(t2, ...) 48 | 49 | if numerator == t1 and denominator == t2 then 50 | return self 51 | elseif type(numerator) == "number" and type(denominator) == "number" then 52 | return numerator % denominator 53 | else 54 | return mod:new{ numerator, denominator } 55 | end 56 | end 57 | 58 | 59 | ------------------------------------------------------------------------------ 60 | 61 | return mod 62 | 63 | ------------------------------------------------------------------------------ 64 | 65 | -------------------------------------------------------------------------------- /lua/tests/rima/operators/call.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local call = require("rima.operators.call") 5 | 6 | local object = require("rima.lib.object") 7 | local lib = require("rima.lib") 8 | local interface = require("rima.interface") 9 | 10 | 11 | ------------------------------------------------------------------------------ 12 | 13 | return function(T) 14 | local E = interface.eval 15 | local D = lib.dump 16 | local R = interface.R 17 | 18 | T:test(object.typeinfo(call:new()).call, "typeinfo(call).call") 19 | T:check_equal(object.typename(call:new()), "call") 20 | 21 | do 22 | local a, b = R"a, b" 23 | 24 | T:check_equal(D(a(b)), "expression(call(index(address{\"a\"}), index(address{\"b\"})))") 25 | T:check_equal(a(b), "a(b)") 26 | T:check_equal(E(a(b)), "a(b)") 27 | end 28 | 29 | do 30 | local f, z = R"f, z" 31 | T:expect_ok(function() f(z) end) 32 | T:check_equal(D(f(z)), "expression(call(index(address{\"f\"}), index(address{\"z\"})))") 33 | T:check_equal(f(z), "f(z)") 34 | T:check_equal(D(E(f(z))), "expression(call(index(address{\"f\"}), index(address{\"z\"})))") 35 | T:check_equal(E(f(z)), "f(z)") 36 | end 37 | 38 | -- The a here ISN'T in the global scope, it's in the function scope 39 | do 40 | local a, f, x = R"a, f, x" 41 | local S = { f = interface.func{a}(2 * a) } 42 | 43 | local c = f(3 + x) 44 | T:check_equal(c, "f(3 + x)") 45 | 46 | T:check_equal(D(c), "expression(call(index(address{\"f\"}), +(1*3, 1*index(address{\"x\"}))))") 47 | T:check_equal(E(c, S), "2*(3 + x)") 48 | S.x = 5 49 | T:check_equal(E(c, S), 16) 50 | end 51 | 52 | do 53 | local f, a, b = R"f, a, b" 54 | local S = { f = string.sub, a = "hello", b = 2 } 55 | T:check_equal(E(f(a, b), S), "ello") 56 | end 57 | end 58 | 59 | 60 | ------------------------------------------------------------------------------ 61 | 62 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- 1 | # Rima Roadmap 2 | 3 | ## Column-wise Modelling 4 | 5 | It's only a short step from the new constraint syntax to column-wise modelling. 6 | `rima.free(), rima.binary()` and others will change (not quite sure how) to 7 | accept an expression: 8 | 9 | model.scope().column_var[{x=X}] = rima.column:new(rima.free(), sum{y=Y}(x * y), cost) 10 | 11 | There's a few tricks: 12 | 13 | + all the free references in a column expression will have to be references to 14 | columns, (as all the free references in constraint expressions will have to 15 | be to columns or variables) 16 | + we'll have to search for columns as well 17 | + rima will accept a mix of row and column-wise modelling, but any non-zero 18 | will have to be defined by only one column or row 19 | + the cost on `column:new` will be optional, and the cost of a variable will 20 | have to be defined by only one of the column or the objective function. 21 | 22 | ## Speed things up 23 | 24 | Constraint search and generation is pretty slow, so one day I'll have to work on 25 | speed a bit. 26 | 27 | ## Better solver support 28 | 29 | This starts with handling a few solver options, and eventually heads towards 30 | resolves. 31 | 32 | ## Type constraints 33 | 34 | Optional type constraints so you can get static checking when you want it. 35 | 36 | ## Host-Language Independence 37 | 38 | It'd be cool if you could bind Rima to languages other than Lua. That might 39 | require a rewrite in C. That's a way off. 40 | 41 | ## Use Information about Model Structure 42 | 43 | We could redefine a knapsack so that it included cover inequalities. That'd 44 | require the Rima "language" complete enough to write a cover generation 45 | heuristic. 46 | 47 | ## Previously on Roadmap 48 | 49 | + Getting Rid of rima.tabulate and rima.default (Done for 0.03) 50 | + Tidying Constraints (Done for 0.03) 51 | + Local Variables (or Objects) (Done for 0.04) 52 | + Subproblems (Done for 0.04) 53 | + Tidying Solve (Partly done for 0.03, more in 0.04) 54 | 55 | -------------------------------------------------------------------------------- /rima-0.03-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "rima" 2 | version = "0.03-1" 3 | source = { 4 | url = "http://rima.googlecode.com/files/rima-0.03.tar.gz" 5 | } 6 | external_dependencies = 7 | { 8 | LIBCOIN = 9 | { 10 | header = "coin/utils/CoinBuild.hpp" 11 | }, 12 | LIBLPSOLVE = 13 | { 14 | header = "lpsolve/lp_lib.h" 15 | } 16 | } 17 | description = 18 | { 19 | summary = "Linear programming for Lua", 20 | detailed = 21 | [[ 22 | Rima is a symbolic math modelling tool. 23 | ]], 24 | homepage = "http://www.incremental.co.nz/projects/lua.html", 25 | license = "MIT/X11", 26 | maintainer = "Geoff Leyland", 27 | } 28 | dependencies = 29 | { 30 | "lua >= 5.1" 31 | } 32 | build = 33 | { 34 | type = "builtin", 35 | modules = 36 | { 37 | rima_clp_core = 38 | { 39 | sources = { "c/rima_clp_core.cpp" }, 40 | libraries = { "clp", "coinutils" }, 41 | incdirs = { "$(LIBCOIN_INCDIR)/coin/clp", "$(LIBCOIN_INCDIR)/coin/utils", "$(LIBCOIN_INCDIR)/coin/headers", }, 42 | libdirs = { "$(LIBCOIN_LIBDIR)"}, 43 | }, 44 | rima_cbc_core = 45 | { 46 | sources = { "c/rima_cbc_core.cpp" }, 47 | libraries = { "cbc", "osiclp" }, 48 | incdirs = { "$(LIBCOIN_INCDIR)/coin/cbc", "$(LIBCOIN_INCDIR)/coin/osi", "$(LIBCOIN_INCDIR)/coin/clp", "$(LIBCOIN_INCDIR)/coin/utils", "$(LIBCOIN_INCDIR)/coin/headers", }, 49 | libdirs = { "$(LIBCOIN_LIBDIR)"}, 50 | }, 51 | rima_lpsolve_core = 52 | { 53 | sources = { "c/rima_lpsolve_core.cpp" }, 54 | libraries = { "lpsolve55", "stdc++" }, 55 | incdirs = { "$(LIBLPSOLVE_INCDIR)/lpsolve", }, 56 | libdirs = { "$(LIBLPSOLVE_LIBDIR)"}, 57 | }, 58 | }, 59 | platforms = 60 | { 61 | win32 = 62 | { 63 | rima_clp_core = { defines = {"NOMINMAX" } }, 64 | rima_cbc_core = { defines = {"NOMINMAX" } }, 65 | rima_lpsolve_core = { defines = {"NOMINMAX" } }, 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lua/test/strict.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- rima's version of strict.lua 3 | -- also found in the lua distribution, in penlight, in idle and discussed 4 | -- here: http://lua-users.org/wiki/DetectingUndefinedVariables 5 | -- 6 | -- In addition to behaving like the original, this version overrides 7 | -- module and setfenv to make any new global environments strict. 8 | -- Because I regularly abuse the module system, it has to be a bit more 9 | -- relaxed with module scopes - sometimes indexing object variables 10 | -- looks like a global access. 11 | -- 12 | 13 | local GLOBAL_SCOPE = getfenv(1) 14 | 15 | local function what () 16 | local d = debug.getinfo(3, "S") 17 | return d and d.what or "C" 18 | end 19 | 20 | local function make_environment_metatable_strict(E) 21 | local mt = getmetatable(E) 22 | if mt == nil then 23 | mt = {} 24 | setmetatable(E, mt) 25 | end 26 | 27 | mt.__declared = mt.__declared or { setfenv=true, module=true } 28 | 29 | if not mt.__newindex then 30 | mt.__newindex = function (t, n, v) 31 | -- only check newindex in the global scope 32 | if t == GLOBAL_SCOPE and not mt.__declared[n] then 33 | local w = what() 34 | if w ~= "main" and w ~= "C" then 35 | error("assign to undeclared variable '"..n.."'", 2) 36 | end 37 | end 38 | mt.__declared[n] = true 39 | rawset(t, n, v) 40 | end 41 | end 42 | 43 | if not mt.__index then 44 | mt.__index = function (t, n) 45 | -- only check if the table appears to be the caller's env 46 | if t == getfenv(2) and not mt.__declared[n] and what() ~= "C" then 47 | error("variable '"..n.."' is not declared", 2) 48 | end 49 | return rawget(t, n) 50 | end 51 | end 52 | end 53 | 54 | make_environment_metatable_strict(getfenv(1)) 55 | 56 | local rawsetfenv = setfenv 57 | function setfenv(f, t) 58 | make_environment_metatable_strict(t) 59 | if type(f) == "number" then f = f + 1 end 60 | rawsetfenv(f, t) 61 | end 62 | local mysetfenv = setfenv 63 | 64 | local rawmodule = module 65 | function module(...) 66 | rawmodule(...) 67 | mysetfenv(2, _M) 68 | end 69 | -------------------------------------------------------------------------------- /lua/rima/operators/call.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local operator = require("rima.operator") 5 | local lib = require("rima.lib") 6 | local core = require("rima.core") 7 | 8 | 9 | ------------------------------------------------------------------------------ 10 | 11 | local call = operator:new_class({}, "call") 12 | 13 | 14 | ------------------------------------------------------------------------------ 15 | 16 | function call:__repr(format) 17 | if format.format == "dump" then 18 | return "call("..lib.concat_repr(self, format)..")" 19 | else 20 | return core.parenthise(self[1], format, 0).."("..lib.concat_repr({unpack(self, 2)}, format)..")" 21 | end 22 | end 23 | 24 | 25 | ------------------------------------------------------------------------------ 26 | 27 | local expression 28 | function call:__eval(...) 29 | local terms = self:evaluate_terms(...) 30 | if not terms then return self end 31 | 32 | if not core.defined(terms[1]) then 33 | return call:new(terms) 34 | else 35 | 36 | local status, value, vtype, addr 37 | if type(terms[1]) == "function" then 38 | expression = expression or require("rima.expression") 39 | status, value, vtype, addr = xpcall(function() return terms[1](expression.vwrap(unpack(terms, 2))) end, debug.traceback) 40 | if value then 41 | value, addr = expression.vunwrap(value, addr) 42 | end 43 | else 44 | local eval_args = {...} 45 | status, value, vtype, addr = xpcall(function() return terms[1]:call({unpack(terms, 2)}, unpack(eval_args)) end, debug.traceback) 46 | end 47 | 48 | if not status then 49 | error(("call: error evaluating '%s' as '%s' with arguments (%s):\n %s"): 50 | format(lib.repr(self), lib.repr(terms[1]), lib.concat_repr({unpack(self, 2)}), value:gsub("\n", "\n ")), 0) 51 | end 52 | return value, vtype, addr 53 | end 54 | end 55 | 56 | 57 | ------------------------------------------------------------------------------ 58 | 59 | return call 60 | 61 | ------------------------------------------------------------------------------ 62 | 63 | -------------------------------------------------------------------------------- /lua/rima/compiler.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2013 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local lib = require("rima.lib") 5 | local core = require("rima.core") 6 | local scope = require("rima.scope") 7 | local index = require("rima.index") 8 | 9 | 10 | ------------------------------------------------------------------------------ 11 | 12 | local function build_scope(variables, arg_name) 13 | arg_name = arg_name or "args" 14 | 15 | local S = scope.new() 16 | local count = 1 17 | for i, v in ipairs(variables) do 18 | if v.sets then 19 | error(("The variable %s is not fully defined"):format(lib.repr(v.ref))) 20 | end 21 | local a = v.ref.address 22 | if #a > 1 then 23 | index.newindex(index:new(S, a:sub(1, -2)), a:value(-1), index:new(nil, arg_name, count)) 24 | else 25 | S[a:value(1)] = index:new(nil, arg_name, count) 26 | end 27 | count = count + 1 28 | end 29 | return S 30 | end 31 | 32 | 33 | local COMPILE_FORMAT = { format = "lua" } 34 | local function stringify(e, S) 35 | return lib.repr(core.eval(e, S), COMPILE_FORMAT) 36 | end 37 | 38 | 39 | local function compile(expressions, variables, arg_names) 40 | arg_names = arg_names or "args" 41 | local S = build_scope(variables) 42 | 43 | local function_string 44 | if not getmetatable(expressions) then 45 | local strings = {} 46 | for i, e in ipairs(expressions) do 47 | strings[i] = stringify(e, S) 48 | end 49 | function_string = "\n {\n "..table.concat(strings, ",\n ").."\n }" 50 | else 51 | function_string = " "..stringify(expressions, S) 52 | end 53 | 54 | function_string = "return function("..arg_names..")\n return"..function_string.."\nend" 55 | 56 | local status, b = pcall(loadstring, function_string) 57 | if not status then 58 | error(("Error compiling following function: %s\n%s"):format(message, function_string)) 59 | end 60 | return b(), function_string 61 | end 62 | 63 | 64 | ------------------------------------------------------------------------------ 65 | 66 | return { compile = compile } 67 | 68 | ------------------------------------------------------------------------------ 69 | -------------------------------------------------------------------------------- /expressions/sum.txt: -------------------------------------------------------------------------------- 1 | E rima.sum{a=$A}($a) 2 | P sum{a in A}(a) 3 | S A = { 1, 2, 3 } 4 | P 6 5 | 6 | E rima.sum{a=$A}($x) 7 | P sum{a in A}(x) 8 | S A = { 1, 2, 3 } 9 | P 3*x 10 | s x = 5 11 | P 15 12 | 13 | E rima.sum{a=$A}($x[$a]) 14 | P sum{a in A}(x[a]) 15 | S A = { 1, 2, 3 } 16 | P x[1] + x[2] + x[3] 17 | s x = { 4, 5, 6 } 18 | P 15 19 | S A = { "a", "b", "c" } 20 | P x.a + x.b + x.c 21 | s x = { a=10, b=20, c=30 } 22 | P 60 23 | 24 | E rima.sum{a=$A}($x[$a]*$y[$a]) 25 | P sum{a in A}(x[a]*y[a]) 26 | S A = { 1, 2, 3 } 27 | P x[1]*y[1] + x[2]*y[2] + x[3]*y[3] 28 | s x = { 4, 5, 6 } 29 | P 4*y[1] + 5*y[2] + 6*y[3] 30 | s y = { 10, 20, 30 } 31 | P 320 32 | S A = { "a", "b", "c" } 33 | P x.a*y.a + x.b*y.b + x.c*y.c 34 | s x = { a=4, b=5, c=6 } 35 | P 4*y.a + 5*y.b + 6*y.c 36 | s y = { a=10, b=20, c=30 } 37 | P 320 38 | 39 | E rima.sum{A=$A}($x[$A]*$y[$A]) 40 | P sum{A in A}(x[A]*y[A]) 41 | S A = { 1, 2, 3 } 42 | P x[1]*y[1] + x[2]*y[2] + x[3]*y[3] 43 | s x = { 4, 5, 6 } 44 | P 4*y[1] + 5*y[2] + 6*y[3] 45 | s y = { 10, 20, 30 } 46 | P 320 47 | S A = { "a", "b", "c" } 48 | P x.a*y.a + x.b*y.b + x.c*y.c 49 | s x = { a=4, b=5, c=6 } 50 | P 4*y.a + 5*y.b + 6*y.c 51 | s y = { a=10, b=20, c=30 } 52 | P 320 53 | 54 | E rima.sum{a=$A}{b=$B}($x[$a][$b]) 55 | P sum{a in A}{b in B}(x[a, b]) 56 | S A = { 1, 2, 3 } 57 | P sum{b in B}(x[1, b] + x[2, b] + x[3, b]) 58 | s B = { "q", "r" } 59 | P x[1].q + x[1].r + x[2].q + x[2].r + x[3].q + x[3].r 60 | s x = {{q=1, r=1},{q=2, r=2},{q=3, r=3}} 61 | P 12 62 | 63 | E rima.sum{a=$A}{b=$B[$a]}($x[$b]) 64 | P sum{a in A}{b in B[a]}(x[b]) 65 | S A = { 1, 2 } 66 | P sum{b in B[1]}(x[b]) + sum{b in B[2]}(x[b]) 67 | s B = {{"q", "r"},{"r", "s"}} 68 | P x.q + 2*x.r + x.s 69 | s x = { q=1, r=2, s=3 } 70 | P 8 71 | 72 | E rima.sum{x=$X}($x) 73 | P sum{x in X}(x) 74 | S X = rima.range(1, $xx) 75 | P sum{x in range(1, xx)}(x) 76 | s xx = 3 77 | P 6 78 | 79 | E rima.sum{x=$X}($x*$y) 80 | P sum{x in X}(x*y) 81 | S X = rima.range(1, $xx) 82 | P sum{x in range(1, xx)}(x*y) 83 | s xx = 5 84 | P 15*y 85 | 86 | -------------------------------------------------------------------------------- /lua/tests/rima/address.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local address = require("rima.address") 5 | 6 | local lib = require("rima.lib") 7 | local object = require("rima.lib.object") 8 | 9 | 10 | ------------------------------------------------------------------------------ 11 | 12 | return function(T) 13 | local function N(...) return address:new(...) end 14 | 15 | -- constructors 16 | T:test(object.typeinfo(N()).address, "typeinfo(address:new()).address") 17 | T:test(object.typeinfo(N("a")).address, "typeinfo(address:new()).address") 18 | T:check_equal(object.typename(N("a")), "address", "typename(address:new())=='address'") 19 | 20 | -- string representation 21 | T:check_equal(N(), "?") 22 | T:check_equal(N("a"), "a") 23 | T:check_equal(N("a", "b"), "a.b") 24 | T:check_equal(N("a", 1), "a[1]") 25 | T:check_equal(N("a", 1, "b"), "a[1].b") 26 | T:check_equal(N("a", 1, 2, "b"), "a[1, 2].b") 27 | T:check_equal(N("a", "b b"), 'a["b b"]') 28 | 29 | T:check_equal(lib.repr(N("a"), {format="dump"}), "address{\"a\"}") 30 | T:check_equal(lib.repr(N("a",1), {format="dump"}), "address{\"a\", 1}") 31 | T:check_equal(lib.repr(N("a",1,2), {format="lua"}), "a[1][2]") 32 | 33 | -- sub 34 | T:check_equal(N("a", "b", "c", "d"):sub(1,3), "a.b.c") 35 | T:check_equal(N("a", "b", "c", "d"):sub(2,3), "b.c") 36 | T:check_equal(N("a", "b", "c", "d"):sub(1,-1), "a.b.c.d") 37 | T:check_equal(N("a", "b", "c", "d"):sub(-2,-1), "c.d") 38 | 39 | -- checking identifiers 40 | T:test(not N():is_identifier()) 41 | T:test(N("a"):is_identifier()) 42 | T:test(not N("a", 1):is_identifier()) 43 | T:test(not N(1):is_identifier()) 44 | T:test(not N(1, "a"):is_identifier()) 45 | T:test(not N("b b"):is_identifier()) 46 | T:test(not N("b b", "a"):is_identifier()) 47 | 48 | -- appending 49 | T:check_equal(address:new("a") + 1 + 2 + "b", "a[1, 2].b") 50 | 51 | -- iterating 52 | local r = {} 53 | for i, a in address:new("a",1,"b"):values() do 54 | r[#r+1] = tostring(a) 55 | end 56 | T:check_equal(table.concat(r, ","), "a,1,b") 57 | 58 | end 59 | 60 | 61 | ------------------------------------------------------------------------------ 62 | 63 | -------------------------------------------------------------------------------- /lua/rima/closure.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local ipairs, require = ipairs, require 5 | 6 | local object = require("rima.lib.object") 7 | local lib = require("rima.lib") 8 | local core = require("rima.core") 9 | local index = require("rima.index") 10 | 11 | module(...) 12 | 13 | local scope = require("rima.scope") 14 | 15 | 16 | -- Closures -------------------------------------------------------------------- 17 | 18 | local closure = object:new_class(_M, "closure") 19 | local counter = 1 20 | 21 | function closure:new(exp, args) 22 | local name = "$closure"..counter 23 | counter = counter + 1 24 | return object.new(closure, { name=name, args=args, exp=args:prepare(exp, name) }) 25 | end 26 | 27 | 28 | function closure:undo(exp, args) 29 | return args:unprepare(exp, self.name) 30 | end 31 | 32 | 33 | function closure:__eval(s) 34 | return core.eval(self.exp, s) 35 | end 36 | 37 | 38 | function closure:set_args(s, args) 39 | local eval_scope = scope.new_local(s) 40 | local local_scope = scope.index(eval_scope, self.name) 41 | local setc = #self.args 42 | local arg_offset = #args - setc 43 | for i = 1, setc do 44 | self.args:set_args(eval_scope, local_scope, i, args[i + arg_offset]) 45 | end 46 | return eval_scope 47 | end 48 | 49 | 50 | function closure:iterate(s) 51 | return self.args:iterate(scope.new_local(s), self.name) 52 | end 53 | 54 | 55 | function closure:fake_iterate(s, undefined) 56 | local sn = scope.new_local(s) 57 | local sf = scope.index(sn, self.name) 58 | for _, v in ipairs(undefined) do 59 | v:fake_iterate(sf) 60 | end 61 | return sn 62 | end 63 | 64 | 65 | function closure:__repr(format) 66 | local ar, er = lib.repr(self.args, format), lib.repr(self.exp, format) 67 | local ff = format.format 68 | local f 69 | if ff == "dump" then 70 | f = "closure("..self.name..", %s, %s)" 71 | elseif ff == "latex" then 72 | f = "%s %s" 73 | else 74 | f = "%s(%s)" 75 | end 76 | 77 | return f:format(ar, er) 78 | end 79 | closure.__tostring = lib.__tostring 80 | 81 | -- Introspection? -------------------------------------------------------------- 82 | 83 | function closure:__list_variables(S, list) 84 | core.list_variables(self.exp, S, list) 85 | end 86 | 87 | 88 | -- EOF ------------------------------------------------------------------------- 89 | 90 | -------------------------------------------------------------------------------- /lua/rima/sets.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local object = require("rima.lib.object") 5 | local operator = require("rima.operator") 6 | local lib = require("rima.lib") 7 | local core = require("rima.core") 8 | local element = require("rima.sets.element") 9 | 10 | 11 | ------------------------------------------------------------------------------ 12 | 13 | local ord = operator:new_class({}, "ord") 14 | 15 | 16 | function ord:__eval(...) 17 | local e = core.eval(self[1], ...) 18 | 19 | if object.typeinfo(e).element then 20 | return element.key(e) 21 | else 22 | if core.defined(e) then 23 | error("ord can only be applied to elements") 24 | else 25 | if e == self[1] then 26 | return self 27 | else 28 | return ord:new{e} 29 | end 30 | end 31 | end 32 | end 33 | 34 | 35 | ------------------------------------------------------------------------------ 36 | 37 | local range_type = object:new_class({}, "range_type") 38 | function range_type:new(l, h) 39 | return object.new(self, { low = l, high = h} ) 40 | end 41 | 42 | 43 | function range_type:__repr(format) 44 | return ("range(%s, %s)"):format(lib.repr(self.low, format), lib.repr(self.high, format)) 45 | end 46 | range_type.__tostring = lib.__tostring 47 | 48 | 49 | local function range_type_iter(high, i) 50 | i = i + 1 51 | if i <= high then return i end 52 | end 53 | 54 | function range_type:__iterate() 55 | -- Cast self.high to a number by adding zero 56 | return range_type_iter, self.high+0, self.low-1 57 | end 58 | 59 | 60 | function range_type:__iterindex(i) 61 | if i < 1 or i > self.high - self.low + 1 then 62 | error(("index out of range trying to index '%s' with '%s'"):format(lib.repr(self), lib.repr(i))) 63 | end 64 | return self.low + i - 1 65 | end 66 | 67 | 68 | local range = operator:new_class({}, "range") 69 | 70 | 71 | function range:__eval(...) 72 | 73 | local l, h = core.eval(self[1], ...), core.eval(self[2], ...) 74 | 75 | if core.defined(l) and core.defined(h) then 76 | return range_type:new(l, h) 77 | else 78 | if l == self[1] and h == self[2] then 79 | return self 80 | else 81 | return range:new{l, h} 82 | end 83 | end 84 | end 85 | 86 | 87 | ------------------------------------------------------------------------------ 88 | 89 | return { ord = ord, range = range } 90 | 91 | ------------------------------------------------------------------------------ 92 | 93 | -------------------------------------------------------------------------------- /docs/index.txt: -------------------------------------------------------------------------------- 1 | # Rima: A Tool for Math Modelling 2 | 3 | Rima is a tool for formulating and solving mathematical modelling and optimization problems. 4 | Rima allows you to build models flexibly and interact with your data freely, 5 | allowing you to focus on the problem at hand and obtain solutions more quickly. 6 | 7 | If you: 8 | 9 | - wish your modelling language had stronger support for general programming features (functions, data structures, more than one model...); 10 | - really like your optimization package for a mainstream language but miss defining your model symbolically; 11 | - wish your purpose-designed modelling language wouldn't make you to treat your data like FORTRAN 66 arrays; 12 | - wonder why your optimization package for a modern, dynamic language still makes you treat your data like FORTRAN 66 arrays; 13 | - find that your modelling system imposes too many dependencies between equations and data; 14 | - just wish you could write a model and then make a subclass of it, or use it as part of another model 15 | 16 | Then Rima might be for you. With Rima you can: 17 | 18 | - specify your model symbolically; 19 | - define your model *before* any data, and define your data for as many instances of your model as you like after the model is defined; 20 | - compose models from parts in the manner that suits you; 21 | - work with your data in the structures that you wish to use; 22 | - access all the features of [Lua](http://www.lua.org/), a fast, small, widely deployed scripting language that's easy to both embed and extend 23 | 24 | Rima binds to well-known optimisation packages to solve problems. 25 | Currently bindings are available for: 26 | 27 | + [CLP](https://projects.coin-or.org/Clp) for linear problems 28 | + [CBC](https://projects.coin-or.org/Cbc) for mixed-integer linear problems 29 | + [lpsolve](http://sourceforge.net/projects/lpsolve) for mixed-integer linear problems 30 | 31 | 32 | ## Find out More 33 | 34 | If Rima has got your attention, 35 | you can [have a look at a Rima model](knapsack.html), 36 | read [Rima's user guide](contents.html) (including example models), 37 | starting with how to [build expressions](expressions.html) 38 | or you can [get Rima](install.html), 39 | 40 | Like Lua, Rima is available under the [MIT Licence](http://www.opensource.org/licenses/mit-license.php), 41 | and source code is freely available. 42 | Rima's [development site](https://github.com/geoffleyland/rima) is hosted at [GitHub](https://github.com/) 43 | 44 | Rima is developed by [Incremental](http://www.incremental.co.nz/). 45 | You can contact us at . 46 | 47 | -------------------------------------------------------------------------------- /lua/rima/mp/constraint.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local math = require("math") 5 | local assert, ipairs, pcall = 6 | assert, ipairs, pcall 7 | 8 | local object = require("rima.lib.object") 9 | local lib = require("rima.lib") 10 | local core = require("rima.core") 11 | local linearise = require("rima.mp.linearise") 12 | local ops = require("rima.operations") 13 | local add_mul = require("rima.operators.add_mul") 14 | 15 | module(...) 16 | 17 | 18 | -------------------------------------------------------------------------------- 19 | 20 | local constraint = object:new_class(_M, "constraint") 21 | 22 | function constraint:new(lhs, rel, rhs) 23 | assert(rel == "==" or rel == ">=" or rel == "<=") 24 | return object.new(self, { type=rel, lhs=lhs, rhs=rhs }) 25 | end 26 | 27 | 28 | function constraint:__eval(S, args) 29 | local lhs = core.eval(self.lhs, S, args) 30 | local rhs = core.eval(self.rhs, S, args) 31 | if lhs == self.lhs and rhs == self.rhs then 32 | return self 33 | else 34 | return constraint:new(lhs, self.type, rhs) 35 | end 36 | end 37 | 38 | 39 | function constraint:characterise(S) 40 | local e = core.eval(ops.add(0, self.lhs, ops.unm(self.rhs)), S) 41 | local rhs = 0 42 | if object.typeinfo(e).add then 43 | local constant, new_e = add_mul.extract_constant(e) 44 | if constant then rhs, e = -constant, new_e end 45 | end 46 | local comp = self.type 47 | local lower = ((comp == "==" or comp == ">=") and rhs) or -math.huge 48 | local upper = ((comp == "==" or comp == "<=") and rhs) or math.huge 49 | 50 | local status, constant, linear_lhs = pcall(linearise.linearise, e, S) 51 | assert(not status or constant==0) 52 | 53 | return lower, upper, e, linear_lhs 54 | end 55 | 56 | 57 | function constraint:tostring(S) 58 | local lhs = lib.repr((core.eval(self.lhs, S))) 59 | local rhs = lib.repr((core.eval(self.rhs, S))) 60 | local s = lhs.." "..self.type.." "..rhs 61 | return s 62 | end 63 | 64 | local latex_compare_translation = 65 | { 66 | ["=="] = "=", 67 | ["<="] = "\\leq", 68 | [">="] = "\\geq", 69 | } 70 | function constraint:__repr(format) 71 | local lhs, rhs = lib.repr(self.lhs, format), lib.repr(self.rhs, format) 72 | local type 73 | if format.format == "latex" then 74 | type = latex_compare_translation[self.type] 75 | else 76 | type = self.type 77 | end 78 | local s = lhs.." "..type.." "..rhs 79 | return s 80 | end 81 | constraint.__tostring = lib.__tostring 82 | 83 | 84 | -- EOF ------------------------------------------------------------------------- 85 | -------------------------------------------------------------------------------- /lua/tests/rima/expression.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local object = require("rima.lib.object") 5 | local lib = require("rima.lib") 6 | local interface = require("rima.interface") 7 | 8 | 9 | ------------------------------------------------------------------------------ 10 | 11 | local function equal(T, expected, got) 12 | local function prep(z) 13 | if object.typename(z) == "table" then 14 | local e = z[1] 15 | for i = 3, #z do 16 | local arg = z[2] 17 | arg = (arg ~= "" and arg) or nil 18 | e = interface.unwrap(z[i](interface.unwrap(e), arg)) 19 | end 20 | return e 21 | else 22 | return z 23 | end 24 | end 25 | 26 | local e, g = prep(expected), prep(got) 27 | T:check_equal(g, e, nil, 1) 28 | end 29 | 30 | return function(T) 31 | local E = interface.eval 32 | local D = lib.dump 33 | local R = interface.R 34 | 35 | -- tests with add, mul and pow 36 | do 37 | local a, b = R"a, b" 38 | local S = {} 39 | equal(T, '+(1*3, 4*index(address{"a"}))', {3 + 4 * a, S, E, D}) 40 | equal(T, "3 - 4*a", {4 * -a + 3, S, E}) 41 | equal(T, '+(1*3, 4**(index(address{"a"})^1, index(address{"b"})^1))', {3 + 4 * a * b, S, E, D}) 42 | equal(T, "3 - 4*a*b", {3 - 4 * a * b, S, E}) 43 | 44 | equal(T, '*(6^1, index(address{"a"})^1)', {3 * (a + a), S, E, D}) 45 | equal(T, "6*a", {3 * (a + a), S, E}) 46 | equal(T, '+(1*1, 6*index(address{"a"}))', {1 + 3 * (a + a), S, E, D}) 47 | equal(T, "1 + 6*a", {1 + 3 * (a + a), S, E}) 48 | 49 | equal(T, '*(1.5^1, index(address{"a"})^-1)', {3 / (a + a), S, E, D}) 50 | equal(T, "1.5/a", {3 / (a + a), S, E}) 51 | equal(T, '+(1*1, 1.5**(index(address{"a"})^-1))', {1 + 3 / (a + a), S, E, D}) 52 | equal(T, "1 + 1.5/a", {1 + 3 / (a + a), S, E}) 53 | 54 | equal(T, '*(3^1, index(address{"a"})^2)', {3 * a^2, "", D}) 55 | equal(T, '*(3^1, index(address{"a"})^2)', {3 * a^2, S, E, D}) 56 | equal(T, '*(3^1, +(1*1, 1*index(address{"a"}))^2)', {3 * (a+1)^2, S, E, D}) 57 | end 58 | 59 | -- tests with references to expressions 60 | do 61 | local a, b = R"a,b" 62 | local S = { b = 3 * (a + 1)^2 } 63 | equal(T, {b, S, E, D}, {3 * (a + 1)^2, S, E, D}) 64 | equal(T, {5 * b, S, E, D}, {5 * (3 * (a + 1)^2), S, E, D} ) 65 | 66 | local c, d = R"c,d" 67 | S.d = 3 + (c * 5)^2 68 | T:expect_ok(function() E(5 * d, S) end) 69 | equal(T, "5*(3 + 25*c^2)", {5 * d, S, E}) 70 | end 71 | 72 | end 73 | 74 | 75 | ------------------------------------------------------------------------------ 76 | 77 | -------------------------------------------------------------------------------- /lua/rima/mp/linearise.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local object = require("rima.lib.object") 5 | local lib = require("rima.lib") 6 | local core = require("rima.core") 7 | local element = require("rima.sets.element") 8 | 9 | 10 | ------------------------------------------------------------------------------ 11 | 12 | local function add_variable(terms, ref, coeff) 13 | local name = lib.repr(ref) 14 | if terms[name] then 15 | error(("the reference '%s' appears more than once"):format(name), 0) 16 | end 17 | terms[name] = { name=name, ref=ref, coeff=coeff } 18 | end 19 | 20 | 21 | local function _linearise(l) 22 | local constant, terms = 0, {} 23 | local fail = false 24 | 25 | local lti = object.typeinfo(l) 26 | 27 | if lti.number then 28 | constant = l 29 | elseif lti.index then 30 | add_variable(terms, l, 1) 31 | elseif lti.element then 32 | local exp = element.expression(l) 33 | add_variable(terms, exp, 1) 34 | elseif lti.add then 35 | for i, a in ipairs(l) do 36 | local c, x = a[1], a[2] 37 | 38 | local xti = object.typeinfo(x) 39 | 40 | if xti.number then 41 | if i ~= 1 then 42 | error(("term %d is constant (%s). Only the first term should be constant"): 43 | format(i, lib.repr(x)), 0) 44 | end 45 | if constant ~= 0 then 46 | error(("term %d is constant (%s), and so is an earlier term. There can only be one constant in the expression"): 47 | format(i, lib.repr(x)), 0) 48 | end 49 | constant = c * x 50 | elseif xti.index then 51 | add_variable(terms, x, c) 52 | elseif xti.element then 53 | local exp = element.expression(x) 54 | add_variable(terms, exp, c) 55 | else 56 | error(("term %d is not linear (got '%s', %s)"):format(i, lib.repr(x), object.typename(x)), 0) 57 | end 58 | end 59 | else 60 | error("the expression does not evaluate to a sum of terms", 0) 61 | end 62 | 63 | return constant, terms 64 | end 65 | 66 | 67 | local function linearise(e, ...) 68 | local l = core.eval(e, ...) 69 | local status, constant, terms = pcall(_linearise, l) 70 | if not status then 71 | error(("Error linearising '%s' (linear form: '%s'):\n %s"):format(lib.repr(e), lib.repr(l), constant:gsub("\n", "\n ")), 0) 72 | end 73 | return constant, terms 74 | end 75 | 76 | 77 | ------------------------------------------------------------------------------ 78 | 79 | return { linearise = linearise } 80 | 81 | ------------------------------------------------------------------------------ 82 | 83 | -------------------------------------------------------------------------------- /docs/arrays.txt: -------------------------------------------------------------------------------- 1 | --! env rima = require("rima") 2 | 3 | # Rima Manual: Arrays 4 | 5 | [ [Contents](contents.html) | Previous: [A Simple LP](simple_lp.html) | Next: [Sums](sums.html) ] 6 | 7 | Array indexing in Rima (and Lua) uses square brackets (`x[1]`) while 8 | array construction uses curly braces (`x = {13, 15, 17}`). 9 | 10 | x = rima.R"x" 11 | print(x[1] + x[2] + x[3]) --> x[1] + x[2] + x[3] 12 | print(rima.E(x[1] + x[2] + x[3], {x={10,20,30}})) --> 60 13 | 14 | If you try to index something that's not an array Rima will complain: 15 | 16 | x = rima.R"x" 17 | print(rima.E(x[1], {x=2})) --# 18 | --> /error evaluating 'x%[1%]':/ error evaluating 'x[1]': 19 | --> /error indexing 'x' as 'x%[1%]': can't index a number/ error indexing 'x' as 'x[1]': can't index a number 20 | 21 | If you wish to use 2D arrays, you need to use two separate sets of square brackets 22 | (`x[2][3]` rather than `x[2, 3]`), 23 | and construct an array of arrays (`x = {{2, 4}, {3, 5}}`). 24 | 25 | x = rima.R"x" 26 | e = x[1][1] + x[2][2] 27 | print(rima.E(e, {x={{5,7},{11,13}}})) --> 18 28 | 29 | So far, we've passed data to `rima.E` in plain Lua tables, 30 | but in fact, rima converts these tables into rima `scopes`. 31 | Scopes offer a number of features beyond what plain tables offer, 32 | and in this case, 33 | they support more complex indexing. 34 | 35 | While it's often convenient (or even necessary) to use scopes rather than plain tables, 36 | in all cases where you're not using any of the extra features of a scope, 37 | you can use a plain table. 38 | 39 | You can assign to a whole array at once using a reference as one of your indices, 40 | but you have to use a scope to get the indexing ability. 41 | Scopes are created with `rima.scope.new`: 42 | 43 | x, i = rima.R"x, i" 44 | S = rima.scope.new() 45 | S.x[i] = 10 -- This won't work with a plain table 46 | print(rima.E(x[1], S), rima.E(x[7], S)) --> 10 10 47 | 48 | Of course, you can compute the values in the array based on the indexes: 49 | 50 | x, i = rima.R"x, i" 51 | S = rima.scope.new() 52 | S.x[i] = 2^i 53 | print(rima.E(x[1], S), rima.E(x[7], S)) --> 2 128 54 | 55 | You can use this method of indexing directly in the call to `rima.scope.new`, 56 | but you have to enclose the index expression in square brackets: 57 | 58 | x, i = rima.R"x, i" 59 | S = rima.scope.new{ [x[i]] = 2^i } 60 | print(rima.E(x[1], S), rima.E(x[7], S)) --> 2 128 61 | 62 | 63 | [ [Contents](contents.html) | Previous: [A Simple LP](simple_lp.html) | Next: [Sums](sums.html) ] 64 | -------------------------------------------------------------------------------- /lua/rima.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local lib = require("rima.lib") 5 | local trace = require("rima.lib.trace") 6 | local core = require("rima.core") 7 | local number_t = require("rima.types.number_t") 8 | local compiler = require("rima.compiler") 9 | local interface = require("rima.interface") 10 | local mp = require("rima.mp") 11 | 12 | 13 | rima.scope = require("rima.scope") 14 | 15 | 16 | ------------------------------------------------------------------------------ 17 | 18 | rima.repr = lib.repr 19 | 20 | ------------------------------------------------------------------------------ 21 | 22 | rima.R = interface.R 23 | rima.define = interface.define 24 | 25 | 26 | ------------------------------------------------------------------------------ 27 | 28 | --- Evaluate an expression 29 | function rima.E(e, S) 30 | local status, r = xpcall(function() return interface.eval(e, S) end, debug.traceback) 31 | if status then 32 | return r 33 | else 34 | trace.reset_depth() 35 | error(("error evaluating '%s':\n %s"):format(lib.repr(e), r:gsub("\n", "\n ")), 2) 36 | end 37 | end 38 | 39 | 40 | ------------------------------------------------------------------------------ 41 | 42 | rima.diff = interface.diff 43 | 44 | ------------------------------------------------------------------------------ 45 | 46 | rima.compile = compiler.compile 47 | 48 | 49 | ------------------------------------------------------------------------------ 50 | 51 | rima.sum = interface.sum 52 | rima.product = interface.product 53 | rima.case = interface.case 54 | rima.min = interface.min 55 | rima.max = interface.max 56 | rima.F = interface.func 57 | rima.ord = interface.ord 58 | rima.range = interface.range 59 | rima.pairs = interface.pairs 60 | rima.ipairs = interface.ipairs 61 | 62 | for k, v in pairs(interface.math) do 63 | if k:sub(1, 1) ~= "_" then 64 | rima[k] = v 65 | end 66 | end 67 | 68 | 69 | ------------------------------------------------------------------------------ 70 | 71 | rima.free = number_t.free 72 | rima.positive = number_t.positive 73 | rima.negative = number_t.negative 74 | rima.integer = number_t.integer 75 | rima.binary = number_t.binary 76 | 77 | 78 | ------------------------------------------------------------------------------ 79 | 80 | rima.mp = 81 | { 82 | C = interface.mp.constraint, 83 | new = mp.new, 84 | solve = mp.solve, 85 | solve_with = mp.solve_with, 86 | } 87 | 88 | 89 | ------------------------------------------------------------------------------ 90 | 91 | 92 | return rima 93 | 94 | ------------------------------------------------------------------------------ 95 | 96 | -------------------------------------------------------------------------------- /lua/tests/rima/types/undefined_t.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local undefined_t = require("rima.types.undefined_t") 5 | 6 | local object = require("rima.lib.object") 7 | local lib = require("rima.lib") 8 | local interface = require("rima.interface") 9 | 10 | 11 | ------------------------------------------------------------------------------ 12 | 13 | return function(T) 14 | local E = interface.eval 15 | local R = interface.R 16 | local sum = interface.sum 17 | 18 | T:test(object.typeinfo(undefined_t:new()).undefined_t, "typeinfo(undefined_t:new()).undefined_t") 19 | T:check_equal(object.typename(undefined_t:new()), 'undefined_t', "typename(undefined_t:new()) == 'undefined_t'") 20 | T:check_equal(undefined_t:new(), 'undefined', "repr(undefined_t:new()) == 'undefined'") 21 | 22 | T:check_equal(undefined_t:new():describe("a"), 'a undefined', "undefined:describe()'") 23 | T:check_equal(undefined_t:new():describe("b"), 'b undefined', "undefined:describe()'") 24 | T:test(undefined_t:new():includes(1), "undefined:includes()'") 25 | T:test(undefined_t:new():includes("a"), "undefined:includes()'") 26 | T:test(undefined_t:new():includes({}), "undefined:includes()'") 27 | T:test(undefined_t:new():includes(nil), "undefined:includes()'") 28 | 29 | do 30 | local x = R"x" 31 | local S = { x = undefined_t:new() } 32 | T:check_equal(E(x, S), "x") 33 | T:check_equal(E(x + 1, S), "1 + x") 34 | end 35 | 36 | do 37 | local x, y, z = R"x, y, z" 38 | local S = { x = { undefined_t:new() }} 39 | local e = sum{y=x}(x[y]) 40 | T:check_equal(E(e, S), "x[1]") 41 | T:check_equal(lib.dump(E(e, S)), "expression(index(address{\"x\", 1}))") 42 | end 43 | 44 | do 45 | local x, y, z = R"x, y, z" 46 | local e = sum{y=x}(y.z) 47 | 48 | local S1 = { x = {{}}} 49 | local S2 = { x = {{z=undefined_t:new()}}} 50 | 51 | T:check_equal(E(e, S1), "x[1].z") 52 | T:check_equal(lib.dump(E(e, S1)), "expression(index(address{\"x\", 1, \"z\"}))") 53 | T:check_equal(E(e, S2), "x[1].z") 54 | T:check_equal(lib.dump(E(e, S2)), "expression(index(address{\"x\", 1, \"z\"}))") 55 | end 56 | 57 | do 58 | local x, X, y, Y, z = R"x, X, y, Y, z" 59 | local e = sum{x=X}(sum{y=x.Y}(y.z)) 60 | 61 | local S1 = { X={{Y={{}}}}} 62 | local S2 = { X={{Y={{z=undefined_t:new()}}}}} 63 | 64 | T:check_equal(E(e, S1), "X[1].Y[1].z") 65 | T:check_equal(lib.dump(E(e, S1)), "expression(index(address{\"X\", 1, \"Y\", 1, \"z\"}))") 66 | T:check_equal(E(e, S2), "X[1].Y[1].z") 67 | T:check_equal(lib.dump(E(e, S2)), "expression(index(address{\"X\", 1, \"Y\", 1, \"z\"}))") 68 | end 69 | end 70 | 71 | 72 | ------------------------------------------------------------------------------ 73 | 74 | -------------------------------------------------------------------------------- /lua/tests/test/series.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local series = require("test.series") 5 | 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | return function(T) 10 | local o2 = {} 11 | for k, v in pairs(T.options) do o2[k] = v end 12 | o2.dont_show_fails = true 13 | local T = series:new(o2, "series") 14 | 15 | local function a(options) 16 | local o2 = {} 17 | for k, v in pairs(options) do o2[k] = v end 18 | if not (options.quiet) then o2.show_passes = true end 19 | local LT = series:new(o2, "test test") 20 | local ok, t, f = LT:close() 21 | T:expect_ok(function() assert(ok == true and t == 0 and f == 0, "empty test") end, "empty test ok") 22 | LT:test(true, "showing a pass") 23 | return LT:close() 24 | end 25 | T:run(a, "path") 26 | 27 | local LT = series:new(o2, "NOT REAL ERRORS") 28 | 29 | -- series:test() 30 | T:expect_ok(function() LT:test(true, "description", "message") end, "series:test") 31 | T:expect_ok(function() LT:test(false, "THIS IS NOT A FAIL", "THIS IS NOT A FAIL") end, "series:test") 32 | T:test(LT:test(true, "description", "message"), "series:test") 33 | T:test(not LT:test(false, "THIS IS NOT A FAIL", "THIS IS NOT A FAIL"), "series:test") 34 | 35 | -- series:check_equal 36 | T:expect_ok(function() LT:check_equal("a", "b", "THIS IS NOT A FAIL") end, "series:check_equal") 37 | T:expect_ok(function() LT:check_equal("a", "a", "THIS IS NOT A FAIL") end, "series:check_equal") 38 | T:expect_ok(function() LT:check_equal(1, 2, "THIS IS NOT A FAIL") end, "series:check_equal") 39 | T:expect_ok(function() LT:check_equal(1, 1, "THIS IS NOT A FAIL") end, "series:check_equal") 40 | 41 | T:test(not LT:check_equal("a", "b", "THIS IS NOT A FAIL"), "series:check_equal") 42 | T:test(LT:check_equal("a", "a", "THIS IS NOT A FAIL"), "series:check_equal") 43 | T:test(not LT:check_equal(1, 2, "THIS IS NOT A FAIL"), "series:check_equal") 44 | T:test(LT:check_equal(1, 1, "THIS IS NOT A FAIL"), "series:check_equal") 45 | 46 | -- series:expect_error 47 | T:expect_ok(function() LT:expect_error(function() error("an error!") end, "another_error") end, "series:expect_error") 48 | T:expect_ok(function() LT:expect_error(function() error("an error!") end, "an error!") end, "series:expect_error") 49 | T:expect_ok(function() LT:expect_error(function() end, "no error") end, "series:expect_error") 50 | 51 | T:test(not LT:expect_error(function() error("an error!") end, "another_error"), "series:expect_error") 52 | T:test(LT:expect_error(function() error("an error!") end, "an error!"), "series:expect_error") 53 | T:test(not LT:expect_error(function() end, "no error"), "series:expect_error") 54 | end 55 | 56 | 57 | ------------------------------------------------------------------------------ 58 | 59 | -------------------------------------------------------------------------------- /docs/simple_lp.txt: -------------------------------------------------------------------------------- 1 | --! env rima = require("rima") 2 | 3 | # Rima Manual: A Simple Linear Program 4 | 5 | [ [Contents](contents.html) | Previous: [Expressions](expressions.html) | Next: [Arrays](arrays.html) ] 6 | 7 | We've got just enough knowledge of expressions to build 8 | our first (very simple) Linear Program and solve it. 9 | 10 | 11 | ## Constructing a Model 12 | 13 | `rima.mp` is the module that handles math programming. 14 | `rima.mp.new` constructs a new model environment: 15 | 16 | M = rima.mp.new() 17 | 18 | The objective and optimisation sense are set as fields of `M`, 19 | which we treat like a Lua table: 20 | 21 | --! continue 22 | x, y = rima.R"x, y" 23 | M.sense = "maximise" 24 | M.objective = x + y 25 | 26 | The string setting the objective sense is not case-sensitive, and can be 27 | maximise, maximize, minimise or minimize. 28 | 29 | `rima.mp.C` creates a new constraint. 30 | Constraints are named, and added to `M` as well: 31 | 32 | --! continue 33 | M.c1 = rima.mp.C(x + 2*y, "<=", 3) 34 | M.c2 = rima.mp.C(2*x + y, "<=", 3) 35 | 36 | Finally we set bounds on the variables: 37 | 38 | --! continue 39 | M.x = rima.positive() 40 | M.y = rima.positive() 41 | 42 | As with expressions, printing a model gives a readable description: 43 | 44 | --! continue 45 | print(M) 46 | --> Maximise: 47 | --> x + y 48 | --> Subject to: 49 | --> c1: x + 2*y <= 3 50 | --> c2: 2*x + y <= 3 51 | --> 0 <= x <= inf, x real 52 | --> 0 <= y <= inf, y real 53 | 54 | 55 | ## Solving a Model 56 | 57 | `rima.mp.solve` solves the model: 58 | 59 | --! continue 60 | primal, dual = rima.mp.solve(M) 61 | 62 | It examines the problem and based on its properties (linearity, integer variables) 63 | chooses the best solver to solve the problem. 64 | (You can influence its choices by setting the preferences of solvers or using 65 | `rima.mp.solve_with') 66 | 67 | `rima.mp.solve` returns two values. 68 | The first is the primal solution to the problem, 69 | the second is the set of duals. 70 | Both are organised using the same set of names as were used to define the problem. 71 | The objective is a field of the primal table: 72 | 73 | --! continue 74 | print(primal.objective) --> 2 75 | 76 | Primal and dual values for the variables can be obtained: 77 | 78 | --! continue 79 | print(primal.x, primal.y) --> 1 1 80 | print(dual.x, dual.y) --> /%-*0 %-*0/ 0 0 81 | 82 | Likewise for the constraints: 83 | 84 | --! continue 85 | print(primal.c1, primal.c2) --> 3 3 86 | print(dual.c2, dual.c2) --> /0%.3+ 0%.3+/ 0.3333 0.3333 87 | 88 | 89 | [ [Contents](contents.html) | Previous: [Expressions](expressions.html) | Next: [Arrays](arrays.html) ] 90 | -------------------------------------------------------------------------------- /docs/sums.txt: -------------------------------------------------------------------------------- 1 | --! env rima = require("rima") 2 | 3 | # Rima Manual: Sums 4 | 5 | [ [Contents](contents.html) | Previous: [Arrays](arrays.html) | Next: [A Blending Problem](blending.html) ] 6 | 7 | Summing the elements of a table is achieved with `rima.sum{tables}(expression)`, 8 | which takes a list of tables to sum over as its first argument, 9 | and an expression to sum as its second: 10 | 11 | x, X = rima.R"x, X" 12 | e = rima.sum{x=X}(x) 13 | print(e) --> sum{x in X}(x) 14 | print(rima.E(e, {X={4, 5, 6}})) --> 15 15 | 16 | The `{x=X}` syntax is especially useful if you want to sum over the same table more than once: 17 | 18 | x1, x2, X = rima.R"x1, x2, X" 19 | e = rima.sum{x1=X}{x2=X}(x1 * x2) 20 | print(rima.E(e, {X={2, 3, 4}})) --> 81 21 | 22 | Often, you'll want your sum expression to operate on data from more than one array: 23 | 24 | i, Y, Z = rima.R"i, Y, Z" 25 | e = rima.sum{i=Y}(Y[i] * Z[i]) 26 | print(rima.E(e, {Y={3,5,7}, Z={7,5,3}})) --> 67 27 | 28 | Notice than in this example, we used the "bound variable" (`i`) as and index to a subscript, 29 | whereas in the previous example, we used the bound variable (`x`) directly in an expression. 30 | 31 | Rima knows the difference between using a bound variable in an index expression, 32 | and as a value, and tries to do the right thing, but it can get confused, 33 | so it's best to approach this with caution. 34 | 35 | As a better, but less familiar alternative, 36 | Rima offers the `ipairs` iterator 37 | (which is much like Lua's `ipairs`). 38 | `ipairs` returns two values per iteration, 39 | the array index and the array value: 40 | 41 | i, x, X = rima.R"i, x, X" 42 | e = rima.sum{["i, x"]=rima.ipairs(X)}(i * x) 43 | print(rima.E(e, {X={3,5,7}})) --> 34 44 | 45 | The syntax for `ipairs` - `["i, x"]=rima.ipairs(X)` is a little convoluted, 46 | but that's the what we have to do to work in with Lua's syntax. 47 | 48 | Alternatively, you can use `rima.ord` to get the index of an array element: 49 | 50 | x, X = rima.R"x, X" 51 | e = rima.sum{x=X}(rima.ord(x) * x) 52 | print(rima.E(e, {X={3,5,7}})) --> 34 53 | 54 | When summing over more than one array, 55 | it's often useful to use a separate array that defines the indexes to sum over. 56 | `rima.sum` works quite hard to do what you mean when you index arrays, 57 | hard enough that arrays can be used like sets in other modelling languages: 58 | 59 | s, S, count = rima.R"s, S, count" 60 | e = rima.sum{s=S}(count[s]) 61 | SS = { "north", "south", "east", "west" } 62 | COUNT = { 1, 2, 4, 8 } 63 | print(rima.E(e, {S=SS, count=COUNT})) --> 15 64 | 65 | [ [Contents](contents.html) | Previous: [Arrays](arrays.html) | Next: [A Blending Problem](blending.html) ] 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rima: A Tool for Math Modelling 2 | 3 | Rima is a tool for formulating and solving mathematical modelling and optimization problems. 4 | Rima's primary design goal is that models should be easy to re-use. 5 | 6 | Rima allows you to build models flexibly and interact with your data freely, 7 | allowing you to focus on the problem at hand and obtain solutions more quickly. 8 | 9 | In a hurry? [Have a look at a Rima model](http://rima.incremental.co.nz/knapsack.html), 10 | or [read the installation instructions](http://rima.incremental.co.nz/install.html). 11 | 12 | ## Features 13 | 14 | If you: 15 | 16 | + wish your modelling language had stronger support for general programming features (functions, data structures, more than one model...) 17 | + really like your optimization package for a mainstream language but miss defining your model symbolically 18 | + wish your purpose-designed modelling language wouldn't make you to treat your data like FORTRAN 66 arrays 19 | + wonder why your optimization package for a modern, dynamic language still makes you treat your data like FORTRAN 66 arrays 20 | + find that your modelling system imposes too many dependencies between equations and data 21 | + just wish you could write a model and then make a subclass of it, or use it as part of another model 22 | 23 | Then Rima might be for you. With Rima you can: 24 | 25 | + specify your model symbolically 26 | + define your model *before* any data, and define your data for as many instances of your model as you like after the model is defined 27 | + compose models from parts in the manner that suits you 28 | + work with your data in the structures that you wish to use 29 | + access all the features of Lua, a fast, small, widely deployed scripting language that's easy to both embed and extend 30 | 31 | Rima binds to well-known optimisation packages to solve problems. 32 | Currently bindings are available for: 33 | 34 | + [Coin CLP](https://projects.coin-or.org/Clp) for linear problems 35 | + [Coin CBC](https://projects.coin-or.org/Cbc) for mixed-integer linear problems 36 | + [lpsolve](http://sourceforge.net/projects/lpsolve) for mixed-integer linear problems 37 | + [Coin IPOPT](https://projects.coin-or.org/Ipopt) for nonlinear problems 38 | 39 | 40 | ## Find out More 41 | 42 | If Rima has got your attention, 43 | you could [have a look at a Rima model](http://rima.incremental.co.nz/knapsack.html), 44 | read the [Rima user guide](http://rima.incremental.co.nz/contents.html) (including example models), 45 | starting with how to [build expressions](http://rima.incremental.co.nz/expressions.html). 46 | You could also [read the installation instructions](http://rima.incremental.co.nz/install.html). 47 | 48 | Rima is developed by [Incremental](http://www.incremental.co.nz/). 49 | You can contact us at . 50 | Rima is available under the [MIT Licence](http://www.opensource.org/licenses/mit-license.php). -------------------------------------------------------------------------------- /lua/rima/operators/pow.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local operator = require("rima.operator") 5 | local lib = require("rima.lib") 6 | local core = require("rima.core") 7 | local mul = require("rima.operators.mul") 8 | local ops = require("rima.operations") 9 | 10 | 11 | ------------------------------------------------------------------------------ 12 | 13 | local pow = operator:new_class({}, "pow") 14 | pow.precedence = 0 15 | 16 | 17 | ------------------------------------------------------------------------------ 18 | 19 | function pow:__repr(format) 20 | local base, exponent = self[1], self[2] 21 | local repr = lib.repr 22 | local paren = core.parenthise 23 | local prec = pow.precedence 24 | 25 | local ff = format.format 26 | if ff == "dump" then 27 | return "^("..repr(base, format)..", "..repr(exponent, format)..")" 28 | elseif ff == "latex" then 29 | return "{"..paren(base, format, prec).."}^{"..repr(exponent, format).."}" 30 | else 31 | return paren(base, format, prec).."^"..paren(exponent, format, prec) 32 | end 33 | end 34 | 35 | 36 | ------------------------------------------------------------------------------ 37 | 38 | function pow:simplify() 39 | local base, exponent = self[1], self[2] 40 | 41 | if base == 0 then 42 | return 0 43 | elseif base == 1 or exponent == 0 then 44 | return 1 45 | elseif exponent == 1 then 46 | return base 47 | elseif type(exponent) == "number" then 48 | return mul:new{{ exponent, base }} 49 | else 50 | return self 51 | end 52 | end 53 | 54 | 55 | function pow:__eval(...) 56 | local t1, t2 = self[1], self[2] 57 | local base, exponent = core.eval(t1, ...), core.eval(t2, ...) 58 | if base == t1 and exponent == t2 then 59 | return self 60 | elseif type(base) == "number" and type(exponent) == "number" then 61 | return base ^ exponent 62 | else 63 | return pow:new{ base, exponent } 64 | end 65 | end 66 | 67 | 68 | ------------------------------------------------------------------------------ 69 | 70 | local log 71 | function pow:__diff(v) 72 | log = log or require"rima.operators.math".log 73 | 74 | local base, exponent = self[1], self[2] 75 | 76 | local base_is_number = type(base) == "number" 77 | 78 | if type(exponent) == "number" then 79 | if base_is_number then return 0 end 80 | return core.eval(ops.mul(exponent, core.diff(base, v), ops.pow(base, exponent - 1))) 81 | end 82 | 83 | if base_is_number then 84 | return core.eval(ops.mul(core.diff(exponent, v), math.log(base), ops.pow(base, exponent))) 85 | end 86 | 87 | return core.eval(ops.mul(core.diff(ops.mul(exponent, log(base)), v), ops.pow(base, exponent))) 88 | end 89 | 90 | 91 | ------------------------------------------------------------------------------ 92 | 93 | return pow 94 | 95 | ------------------------------------------------------------------------------ 96 | 97 | -------------------------------------------------------------------------------- /lua/rima/operators/case.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local operator = require("rima.operator") 5 | local lib = require("rima.lib") 6 | local core = require("rima.core") 7 | local undefined_t = require("rima.types.undefined_t") 8 | 9 | 10 | ------------------------------------------------------------------------------ 11 | 12 | local case = operator:new_class({}, "case") 13 | case.precedence = 1 14 | 15 | 16 | ------------------------------------------------------------------------------ 17 | 18 | function case:__repr(format) 19 | local s = ("case %s ("):format(lib.repr(self[1], format)) 20 | for _, v in ipairs(self[2]) do 21 | s = s..("%s: %s; "):format(lib.repr(v[1], format), lib.repr(v[2], format)) 22 | end 23 | if rawget(self, 3) then 24 | s = s..("default: %s; "):format(lib.repr(self[3])) 25 | end 26 | s = s..")" 27 | return s 28 | end 29 | 30 | 31 | ------------------------------------------------------------------------------ 32 | 33 | local function eval_preserve(value, type, addr, ...) 34 | local t, a 35 | value, t, a = core.eval(value, ...) 36 | return value, t or type, a or addr 37 | end 38 | 39 | 40 | function case:__eval(...) 41 | -- Evaluate the test value 42 | local value = core.eval(self[1], ...) 43 | local value_defined = core.defined(value) 44 | 45 | -- Run through evaluating all the case values, seeing if we get a match 46 | local cases = {} 47 | local found_match 48 | for i, v in ipairs(self[2]) do 49 | local match_value = core.eval(v[1], ...) 50 | if value_defined and core.defined(match_value) then 51 | if value == match_value then 52 | local value, type, addr = eval_preserve(v[2], v[3], v[4], ...) 53 | if #cases == 0 then -- if it's the first match, and none of the 54 | -- preceeding ones were undefined, we're done 55 | return value, type, addr 56 | end 57 | -- otherwise, collect it and stop 58 | cases[#cases+1] = { match_value, value, type, addr } 59 | found_match = true 60 | break 61 | end 62 | else -- if we can't compare it, collect it 63 | cases[#cases+1] = { match_value, eval_preserve(v[2], v[3], v[4], ...) } 64 | end 65 | end 66 | -- if no cases matched, return the default (if there is one) 67 | if #cases == 0 and self[3] then 68 | return eval_preserve(self[3], self[4], self[5], ...) 69 | end 70 | 71 | -- only keep the default if we didn't find a match (we might be here if we 72 | -- did find a match, but earlier match values weren't defined) 73 | if found_match or not self[3] then 74 | return case:new{value, cases} 75 | else 76 | return case:new{value, cases, eval_preserve(self[3], self[4], self[5], ...)} 77 | end 78 | end 79 | 80 | 81 | ------------------------------------------------------------------------------ 82 | 83 | return case 84 | 85 | ------------------------------------------------------------------------------ 86 | 87 | 88 | -------------------------------------------------------------------------------- /docs/expressions.txt: -------------------------------------------------------------------------------- 1 | --! env rima = require("rima") 2 | 3 | # Rima Manual: Expressions 4 | 5 | [ [Contents](contents.html) | Previous: [References](references.html) | Next: [A Simple LP](simple_lp.html) ] 6 | 7 | *Expressions* are Rima's fundamental construct. 8 | They are stored symbolically and 9 | can be evaluated with partial or full data at any time. 10 | Expressions are built from references (to data or variables), 11 | arithmetic operators and functions and by combining other expressions. 12 | 13 | 14 | ## Defining an Expression 15 | 16 | Defining an expression in Rima is straightforward: 17 | 18 | rima.define"x, y" 19 | e = x + y 20 | print(e) --> x + y 21 | 22 | Normal math operators (`+`, `-`, `*`, `/`) all work, 23 | and you can use all the functions in the Lua math library, 24 | though you have you'll have to use the Rima versions: 25 | 26 | print(rima.sin(rima.R"x")) --> sin(x) 27 | 28 | Expressions can be treated just like variables, and used to build more complex expressions: 29 | 30 | x, y = rima.R"x, y" 31 | e = 2 + y 32 | print(x * e) --> (2 + y)*x 33 | 34 | 35 | ## Evaluating Expressions 36 | 37 | `rima.E` evaluates expressions as it does references: 38 | 39 | rima.define"x, y" 40 | print(rima.E(x + y, { x=1, y=2 })) --> 3 41 | 42 | Partial lists of values are fine - `rima.E` doesn't mind if some variables aren't defined: 43 | 44 | x, y = rima.R"x, y" 45 | print(rima.E(x * rima.sin(y), { x=5 })) --> 5*sin(y) 46 | 47 | And sometimes it doesn't matter if they're defined or not: 48 | 49 | x, y = rima.R"x, y" 50 | print(rima.E(x * y * y / y^2 - x)) --> 0 51 | 52 | If an expression is completely defined, 53 | that is, 54 | all the references in the expression resolve to numbers, 55 | then `rima.E` returns a number. 56 | If not, `rima.E` returns another expression, 57 | which you can evaluate later: 58 | 59 | x, y = rima.R"x, y" 60 | e = rima.E(x + y, { x=7 }) 61 | print(e) --> 7 + y 62 | print(rima.E(e, { y=13 })) --> 20 63 | 64 | `rima.E` will simplify expressions, even without a scope: 65 | 66 | x, y = rima.R"x, y" 67 | e = x * x * y * y^2 68 | print(e) --> x^2*y^3 69 | print(rima.E(e)) --> x^2*y^3 70 | 71 | 72 | ## References to Expressions 73 | 74 | Just as the values in the scope can be references, 75 | they can also be expressions, 76 | allowing you to build complex expressions from parts 77 | and meaning you can leave some parts of an expression to be defined later: 78 | 79 | rima.define"x, y, z" 80 | e = x / y 81 | print(rima.E(e, { y=z^2 })) --> x/z^2 82 | print(rima.E(e, { y=z^2, z=x^0.5 })) --> 1 83 | 84 | [ [Contents](contents.html) | Previous: [References](references.html) | Next: [A Simple LP](simple_lp.html) ] 85 | -------------------------------------------------------------------------------- /lua/rima/func.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local object = require("rima.lib.object") 5 | local lib = require("rima.lib") 6 | local core = require("rima.core") 7 | local scope = require("rima.scope") 8 | local index = require("rima.index") 9 | 10 | 11 | ------------------------------------------------------------------------------ 12 | 13 | local func = object:new_class({}, "func") 14 | local counter = 1 15 | 16 | 17 | local function read(args) 18 | local new_args = {} 19 | for i, v in ipairs(args) do 20 | if type(v) == "string" then 21 | new_args[i] = v 22 | elseif object.typeinfo(v).index then 23 | if index:is_identifier(v) then 24 | new_args[i] = lib.repr(v) 25 | else 26 | error(("bad input #%d to function constructor: expected string or identifier, got '%s' (%s)"): 27 | format(i, lib.repr(v), object.typename(v)), 0) 28 | end 29 | else 30 | error(("bad input #%d to function constructor: expected string or identifier, got '%s' (%s)"): 31 | format(i, lib.repr(v), object.typename(v)), 0) 32 | end 33 | end 34 | return new_args 35 | end 36 | 37 | 38 | local function prepare(exp, name, args) 39 | if args[1] then 40 | local S2 = scope.new() 41 | for i, a in ipairs(args) do 42 | scope.newindex(S2, a, index:new(nil, name, a)) 43 | end 44 | exp = core.eval(exp, S2) 45 | end 46 | return exp 47 | end 48 | 49 | 50 | function func:new(args, exp, S) 51 | local name = "$func"..counter 52 | counter = counter + 1 53 | args = read(args) 54 | 55 | if S then exp = core.eval(exp, S) end 56 | 57 | return object.new(self, { name=name, args=args, exp=prepare(exp, name, args) }) 58 | end 59 | 60 | 61 | ------------------------------------------------------------------------------ 62 | 63 | function func:__repr(format) 64 | return ("function(%s) return %s"): 65 | format(lib.concat_repr(self.args, format), lib.repr(self.exp, format)) 66 | end 67 | func.__tostring = lib.__tostring 68 | 69 | ------------------------------------------------------------------------------ 70 | 71 | 72 | function func:call(args, S) 73 | if not args then return self end 74 | S = (S and not self.args[1]) and S or scope.new(S) 75 | local Sn = scope.index(S, self.name) 76 | 77 | local new_args = {} 78 | for i, n in ipairs(self.args) do 79 | local a = args[i] 80 | if a then 81 | index.newindex(Sn, n, a) 82 | else 83 | index.newindex(Sn, n, index:new(nil, n)) 84 | new_args[#new_args+1] = n 85 | end 86 | end 87 | 88 | local exp = core.eval_to_paths(self.exp, S) 89 | return new_args[1] and func:new(new_args, exp) or exp 90 | end 91 | 92 | 93 | ------------------------------------------------------------------------------ 94 | 95 | local expression = require"rima.expression" 96 | 97 | function func:__call(...) 98 | return self:call(expression.vtunwrap(...)) 99 | end 100 | 101 | 102 | ------------------------------------------------------------------------------ 103 | 104 | return func 105 | 106 | ------------------------------------------------------------------------------ 107 | 108 | -------------------------------------------------------------------------------- /docs/structures.txt: -------------------------------------------------------------------------------- 1 | --! env rima = require("rima") 2 | 3 | # Rima Manual: Structures 4 | 5 | [ [Contents](contents.html) | Previous: [A Blending Problem](blending.html) | Next: [A Structured Knapsack](knapsack_2.html) ] 6 | 7 | Lua's arrays are implemented as "tables". 8 | Tables are efficient as both a numerically indexed array, 9 | and as a hash lookup or dictionary. 10 | 11 | You can index the "hash part" of the table with the same syntax as the "array part": 12 | `x["key"]` works as well as `x[1]`. 13 | For string keys you can use dot notation: `x.key` is exactly the same as `x["key"]`. 14 | 15 | Rima understands structures as well as arrays, so you can treat references like objects: 16 | 17 | x = rima.R"x" 18 | volume = x.length * x.width * x.height 19 | print(rima.E(volume, {x={length=2,width=5}})) --> 10*x.height 20 | 21 | Just like 2 and higher dimensional arrays, you can construct complex tables, 22 | and index them with several indices: 23 | 24 | x = rima.R"x" 25 | e = x[1].a + x[2].b 26 | print(rima.E(e, {x={{a=17},{b=19}}})) --> 36 27 | 28 | The subscripts you use in indexes don't have to be literal values, 29 | they can be references to other variables: 30 | 31 | x, y = rima.R"x, y" 32 | e = x[y].a 33 | print(rima.E(e, {y="second"})) --> x.second.a 34 | print(rima.E(e, {y="second", x={first={a=20},second={a=50}}})) 35 | --> 50 36 | 37 | Beware of confusing `x[a]` (where `a` is a reference) 38 | with `x["a"]` and `x.a` (where `a` is a string key) though! 39 | 40 | 41 | `rima.sum` understands structures as well as arrays. 42 | You can sum over expressions about structures: 43 | 44 | x, X = rima.R"x, X" 45 | e = rima.sum{x=X}(x.y * x.z) 46 | XX = {{y=3, z=7}, {y=5, z=5}, {y=7, z=3}} 47 | print(rima.E(e, {X=XX})) --> 67 48 | 49 | Or just sum the elements of a structure: 50 | 51 | x, X = rima.R"x, X" 52 | e = rima.sum{x=X}(x) 53 | XX = {apples=2,oranges=3,bananas=4} 54 | print(rima.E(e, {X=XX})) --> 9 55 | 56 | So you can be more descriptive of your data: 57 | 58 | i, items = rima.R"i, items" 59 | e = rima.sum{i=items}(i.cost * i.quantity) 60 | ITEMS = {apples={cost=1, quantity=2}, oranges={cost=2, quantity=5}} 61 | print(rima.E(e, {items=ITEMS})) --> 12 62 | 63 | As usual, Rima doesn't mind if some of the data isn't defined: 64 | 65 | i, items = rima.R"i, items" 66 | e = rima.sum{i=items}(i.cost * i.quantity) 67 | ITEMS = {apples={cost=1}, oranges={quantity=2}} 68 | print(rima.E(e, {items=ITEMS})) --> items.apples.quantity + 2*items.oranges.cost 69 | 70 | In the section on arrays, 71 | we saw that an array can be indexed with the elements of a separate table. 72 | The same thing works for tables with non-integer indexes: 73 | 74 | s, S, count, cost = rima.R"s, S, count, cost" 75 | e = rima.sum{s=S}(count[s] * cost[s]) 76 | SS = { "north", "south", "east", "west" } 77 | COUNT = { 1, 2, 4, 8 } 78 | COST = { north=3, south=2, east=1, west=1 } 79 | print(rima.E(e, {S=SS, count=COUNT, cost=COST})) --> 19 80 | 81 | [ [Contents](contents.html) | Previous: [A Blending Problem](blending.html) | Next: [A Structured Knapsack](knapsack_2.html) ] 82 | -------------------------------------------------------------------------------- /lua/rima/operators/math.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local object = require("rima.lib.object") 5 | local operator = require("rima.operator") 6 | local lib = require("rima.lib") 7 | local core = require("rima.core") 8 | local ops = require("rima.operations") 9 | 10 | local rmath = {} 11 | 12 | ------------------------------------------------------------------------------ 13 | 14 | local math_functions = 15 | { 16 | "exp", "log", "log10", 17 | "sin", "asin", "cos", "acos", "tan", "atan", 18 | "sinh", "cosh", "tanh", 19 | "sqrt", 20 | } 21 | 22 | 23 | local function math_repr(args, format) 24 | local o = object.typename(args) 25 | local ff = format.format 26 | if ff == "latex" then 27 | if o == "sqrt" then 28 | return "\\sqrt{"..lib.repr(args[1], format).."}" 29 | elseif o == "exp" then 30 | return "e^{"..lib.repr(args[1], format).."}" 31 | elseif o == "log" then 32 | return "\\ln "..core.parenthise(args[1], format, 1) 33 | elseif o == "log10" then 34 | return "\\log_{10} "..core.parenthise(args[1], format, 1) 35 | else 36 | if #args > 1 then 37 | return "\\"..object.type(args).."("..lib.concat_repr(args, format)..")" 38 | else 39 | return "\\"..object.type(args).." "..core.parenthise(args[1], format, 1) 40 | end 41 | end 42 | elseif ff == "lua" then 43 | return "math."..o.."("..lib.concat_repr(args, format)..")" 44 | else 45 | return o.."("..lib.concat_repr(args, format)..")" 46 | end 47 | end 48 | 49 | 50 | local function math_diff(args, v) 51 | local o = object.typename(args) 52 | local a = args[1] 53 | 54 | local dadv = core.diff(a, v) 55 | if dadv == 0 then return 0 end 56 | 57 | if o == "exp" then 58 | return ops.mul(dadv, rmath.exp(a)) 59 | elseif o == "log" then 60 | return ops.div(dadv, a) 61 | elseif o == "log10" then 62 | return ops.div(dadv, ops.mul(math.log(10), a)) 63 | elseif o == "sin" then 64 | return ops.mul(dadv, rmath.cos(a)) 65 | elseif o == "cos" then 66 | return ops.mul(-1, dadv, rmath.sin(a)) 67 | elseif o == "sqrt" then 68 | return mul:new(dadv, 0.5, ops.pow(a, -0.5)) 69 | else 70 | error("Can't differentiate "..o, 0) 71 | end 72 | end 73 | 74 | 75 | local function make_math_function(name) 76 | local f = assert(math[name], "The math function does not exist") 77 | local op = operator:new_class({ precedence = 0 }, name) 78 | 79 | op.simplify = function(self) 80 | if type(self[1]) == "number" then 81 | return f(self[1]) 82 | else 83 | return self 84 | end 85 | end 86 | 87 | op.__eval = function(self, ...) 88 | local e = core.eval(self[1], ...) 89 | if e == self[1] then return self end 90 | return op:new{e} 91 | end 92 | 93 | op.__repr = math_repr 94 | op.__diff = math_diff 95 | 96 | rmath[name] = function(e) 97 | return op:new{e} 98 | end 99 | end 100 | 101 | 102 | for _, name in ipairs(math_functions) do 103 | make_math_function(name) 104 | end 105 | 106 | 107 | ------------------------------------------------------------------------------ 108 | 109 | return rmath 110 | 111 | ------------------------------------------------------------------------------ 112 | 113 | -------------------------------------------------------------------------------- /lua/rima/sets/element.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local require, setmetatable = require, setmetatable 5 | 6 | local object = require("rima.lib.object") 7 | local lib = require("rima.lib") 8 | local core = require("rima.core") 9 | 10 | local typeinfo = object.typeinfo 11 | 12 | module(...) 13 | 14 | 15 | -- Elements ------------------------------------------------------------------- 16 | 17 | element = object:new_class(_M, "element") 18 | local elements = setmetatable({}, { __mode="k" }) 19 | 20 | function element:new(exp, key, value) 21 | local e = { exp=exp, key=key, value=value } 22 | local p = object.new(element, {}) 23 | elements[p] = e 24 | return p 25 | end 26 | 27 | 28 | function element:key() return elements[self].key end 29 | function element:value() return elements[self].value end 30 | function element:expression() return elements[self].exp end 31 | 32 | function element:display() 33 | self = elements[self] 34 | local v = self.value 35 | if v then 36 | local t = typeinfo(v) 37 | if not t.undefined_t and not t.table then 38 | return v 39 | end 40 | end 41 | return self.key 42 | end 43 | 44 | 45 | function element:__eval(...) 46 | local s = elements[self] 47 | if s.value then return self end 48 | 49 | local value, _, exp = core.eval(s.exp, ...) 50 | if value == s.value and lib.repr(exp) == lib.repr(s.exp) then 51 | return self 52 | end 53 | 54 | value = core.defined(value) and value or nil 55 | return element:new(exp or s.exp, s.key, value) 56 | end 57 | 58 | 59 | function element:__defined() 60 | return elements[self].value ~= nil 61 | end 62 | 63 | 64 | function element:__arithmetic() 65 | return core.arithmetic(elements[self].value) 66 | end 67 | 68 | 69 | function element:__repr(format) 70 | self = elements[self] 71 | if format.format == "dump" then 72 | return ("element(%s, key=%s, value=%s)"): 73 | format(lib.repr(self.exp, format), lib.repr(self.key, format), lib.repr(self.value, format)) 74 | else 75 | if core.arithmetic(self.value) then 76 | return lib.repr(self.value, format) 77 | else 78 | return lib.repr(self.exp, format) 79 | end 80 | end 81 | end 82 | element.__tostring = lib.__tostring 83 | 84 | 85 | -- Operators ------------------------------------------------------------------- 86 | 87 | function element:extract() 88 | self = elements[self] 89 | local v = self.value 90 | if not v or typeinfo(v).undefined_t then 91 | return self.exp 92 | else 93 | return v 94 | end 95 | end 96 | 97 | 98 | local function ex(a) 99 | return lib.convert(a, "extract") 100 | end 101 | 102 | 103 | function element.__add(a, b) return ex(a) + ex(b) end 104 | function element.__sub(a, b) return ex(a) - ex(b) end 105 | function element.__mul(a, b) return ex(a) * ex(b) end 106 | function element.__div(a, b) return ex(a) / ex(b) end 107 | function element.__pow(a, b) return ex(a) ^ ex(b) end 108 | 109 | function element:__unm() return -elements[self].value end 110 | 111 | local index 112 | function element:__index(i) 113 | index = index or require"rima.index" 114 | return index:new(elements[self].exp, i) 115 | end 116 | 117 | 118 | -- EOF ------------------------------------------------------------------------- 119 | 120 | -------------------------------------------------------------------------------- /lua/rima/lib/object.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2013 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | --- Yet *another* Lua type system. 5 | -- This one uses the type system described in PiL, and adds a `__typename` 6 | -- field to the metatable with the object's typename and a `__typeinfo` 7 | -- field that's a set of all the object's and its ancestors' names, 8 | -- and all their metatables. 9 | -- @module rima.lib.object 10 | 11 | 12 | ------------------------------------------------------------------------------ 13 | 14 | --- typeinfo data for the core Lua types. 15 | local core_type_info = 16 | { 17 | boolean = { boolean = true }, 18 | ["function"] = { ["function"] = true }, 19 | ["nil"] = { ["nil"] = true }, 20 | number = { number = true }, 21 | string = { string = true }, 22 | table = { table = true }, 23 | thread = { thread = true }, 24 | userdata = { userdata = true }, 25 | } 26 | 27 | 28 | ------------------------------------------------------------------------------ 29 | 30 | local object = {} 31 | object.__index = object 32 | object.__typename = "object" 33 | object.__typeinfo = { object = true, [object] = true } 34 | 35 | 36 | --- Create a new class. 37 | -- Create a subclass of self. Set `__typename` and `__typeinfo`. 38 | -- `__typeinfo` is copied from self and the new typename and class object 39 | -- are added. 40 | -- @treturn table: the new class. 41 | function object:new_class( 42 | o, -- ?table: the new class object. 43 | new_type_name) -- ?string: the name of the new class. 44 | o = o or {} 45 | if new_type_name then o.__typename = new_type_name end 46 | local parent_typeinfo = self.__typeinfo 47 | o.__typeinfo = { [o.__typename] = true, [o] = true } 48 | if self.__typeinfo then 49 | for k, v in pairs(self.__typeinfo) do 50 | o.__typeinfo[k] = v 51 | end 52 | end 53 | o.__index = o 54 | return setmetatable(o, self) 55 | end 56 | 57 | 58 | --- Create a new object. 59 | -- All the heavy lifting was done in @{rima.lib.object.object:new_class}, so just 60 | -- set the new object's metatable. 61 | -- @treturn table: the new object. 62 | function object:new( 63 | o) -- ?table: the table to turn into the class. 64 | return setmetatable(o or {}, self) 65 | end 66 | 67 | 68 | --- Get a value's typeinfo. 69 | -- if a value `v` is of type `T` then 70 | -- `object.typeinfo(v).T == true`. 71 | -- This works for the core Lua types. 72 | -- @treturn table: typeinfo about the value. 73 | function object.typeinfo( 74 | v) -- ?anything: the value to get typeinfo for. 75 | local mt = getmetatable(v) 76 | return mt and mt.__typeinfo or core_type_info[type(v)] 77 | end 78 | 79 | 80 | --- Return a value's typename. 81 | -- This works for the core Lua types. 82 | -- @treturn string: the typename of the value. 83 | function object.typename( 84 | v) -- ?anything: the value to get typeinfo for. 85 | local mt = getmetatable(v) 86 | return mt and mt.__typename or type(v) 87 | end 88 | 89 | 90 | ------------------------------------------------------------------------------ 91 | 92 | return object 93 | 94 | ------------------------------------------------------------------------------ 95 | 96 | -------------------------------------------------------------------------------- /lua/examples/minimax.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | require("rima") 5 | 6 | -------------------------------------------------------------------------------- 7 | 8 | -- Set up references to variables 9 | local P, p = rima.R"P, p" -- set of sampling Points 10 | local T, t = rima.R"T, t" -- set of Terms we're trying to fit 11 | local s = rima.R"s" -- y-coordinates of Samples 12 | local y = rima.R"y" -- Y-coordinates of functions at each point 13 | 14 | local w = rima.R"w" -- set of Weights we're trying to find 15 | local max_error = rima.R"max_error" -- max error 16 | local sum_error = rima.R"sum_error" -- sum of errors 17 | local Q, q = rima.R"Q, q" -- positive and negative error set 18 | local e = rima.R"e" -- positive and negative Errors at each point 19 | 20 | 21 | -- Curve fit formulation 22 | local curve_fit = rima.mp.new() 23 | curve_fit.fit_data[{p=P}] = rima.mp.C( 24 | rima.sum{t=T}(w[t] * y[t][p]) + 25 | rima.sum{q=Q}(q * e[q][p]), "==", 26 | s[p]) 27 | curve_fit.find_max_error[{q=Q}][{p=P}] = rima.mp.C(max_error, ">=", e[q][p]) 28 | curve_fit.find_error_sum = rima.mp.C(sum_error, "==", rima.sum{q=Q, p=P}(e[q][p])) 29 | curve_fit.max_error = rima.positive() 30 | curve_fit.sum_error = rima.positive() 31 | curve_fit.Q = {-1, 1} 32 | curve_fit.e[{q=Q}][{p=P}] = rima.positive() 33 | curve_fit.w[{t=T}] = rima.free() 34 | curve_fit.sense = "minimise" 35 | 36 | -- Write the formulation 37 | io.write("\nCurve Fitting\n", tostring(curve_fit), "\n") 38 | --[[ output: 39 | No objective defined 40 | Subject to: 41 | -e[-1, p] + e[1, p] + sum(w[t]*y[t, p], t in T) == s[p] for all p in P 42 | max_error >= e[-1, p] for all p in P 43 | max_error >= e[1, p] for all p in P 44 | sum_error == sum(e[-1, p] + e[1, p], p in P) 45 | --]] 46 | 47 | -- minimax formulation 48 | local minimax = rima.mp.new(curve_fit, { objective = max_error }) 49 | 50 | -- minisum formulation 51 | local minisum = rima.mp.new(curve_fit, { objective = sum_error }) 52 | 53 | -- equally spaced sampling points 54 | local points, x, xmin, xmax = rima.R"points, x, xmin, xmax" 55 | local equispaced_points = 56 | { 57 | P = rima.range(0, points), 58 | [x[p]] = xmin + (xmax - xmin) * p / points 59 | } 60 | 61 | -- fit a polynomial with arbitrary order terms 62 | local polynomial_fits = { [y[t][p]] = x[p]^t } 63 | 64 | -- fit polynomial with terms 1, x, x^2 65 | local terms = rima.R"terms" 66 | local consecutive_polynomials = { T = rima.range(1, terms) } 67 | 68 | -- fit to a function 69 | local f = rima.R"f" 70 | local samples_from_function = { [s[p]] = f(x[p]) } 71 | --local samples_from_function = { [s[p]] = x[p] } 72 | 73 | -- Put it all together 74 | local Z = rima.mp.new(minimax, polynomial_fits, consecutive_polynomials, samples_from_function, equispaced_points) 75 | 76 | local primal, dual = rima.mp.solve(Z, 77 | { 78 | xmin = 0, 79 | xmax = 10, 80 | points = 10, 81 | terms = 4, 82 | f = rima.F{x}(rima.exp(x) * rima.sin(x)), 83 | }) 84 | if primal then 85 | io.write(("\nMinimax Polynomial\n max error:\t% 10.2f\n"):format(primal.objective)) 86 | for k, v in pairs(primal.w) do io.write((" w[%d]:\t% 10.2f\t(% 10.2f)\n"):format(k, v, dual.w[k])) end 87 | end 88 | 89 | -- EOF ------------------------------------------------------------------------- 90 | 91 | -------------------------------------------------------------------------------- /docs/blending.txt: -------------------------------------------------------------------------------- 1 | --! env rima = require("rima") 2 | 3 | # Rima Manual: A Blending Problem 4 | 5 | [ [Contents](contents.html) | Previous: [Sums](Sums.html) | Next: [Structures](structures.html) ] 6 | 7 | We wish to make a dog food that meets nutritional requirements at lowest cost 8 | by blending a number of ingredients, 9 | each of which is composed of a number of nutrients. 10 | 11 | (This problem is courtesy of an example by Mike Trick) 12 | 13 | We'll construct this model using only arrays, 14 | but after the next sections on structures, 15 | you'll see there's a cleaner way to structure the model. 16 | 17 | First, we declare some references: 18 | 19 | i, I = rima.R"i, I" -- ingredients 20 | n, N = rima.R"n, N" -- nutrients 21 | c = rima.R"c" -- cost of ingredients 22 | pn = rima.R"pn" -- nutrients in each product 23 | l = rima.R"l" -- limits on nutrients 24 | Q = rima.R"Q" -- Quantity of product we're producing 25 | f = rima.R"f" -- ingredient fractions (result) 26 | 27 | Then we set up our problem: 28 | 29 | --! continue 30 | blending_problem = rima.mp.new{ \ 31 | sense = "minimise", \ 32 | objective = rima.sum{i=I}(c[i] * f[i]), \ 33 | make_quantity = rima.mp.C(rima.sum{i=I}(f[i]), "==", Q) \ 34 | } 35 | blending_problem.sufficient_nutrients[{n=N}] = rima.mp.C(rima.sum{i=I}(pn[i][n] * f[i]), ">=", Q * l[n]) 36 | blending_problem.f[{i=I}] = rima.positive() 37 | 38 | Note that the last two lines are array assignments for a constraint and 39 | for variable bounds. 40 | 41 | As before, we can print out the blending problem: 42 | 43 | --! continue 44 | print(blending_problem) 45 | --> Minimise: 46 | --> sum{i in I}(c[i]*f[i]) 47 | --> Subject to: 48 | --> make_quantity: sum{i in I}(f[i]) == Q 49 | --> sufficient_nutrients[n in N]: sum{i in I}(f[i]*pn[i, n]) >= Q*l[n] 50 | --> 0 <= f[i] <= inf, f[i] real for all i in I 51 | 52 | Next, we define the data for the model: 53 | 54 | --! continue 55 | whiskas_data = { \ 56 | I = {"chicken", "beef", "mutton", "rice", "wheat bran", "gel"}, \ 57 | N = {"protein", "fat", "fibre", "salt"}, \ 58 | c = { 0.013, 0.008, 0.010, 0.002, 0.005, 0.001 }, \ 59 | l = { 0.08, 0.06, -0.02, -0.004 }, \ 60 | pn = \ 61 | { \ 62 | chicken = { 0.100, 0.080, -0.001, -0.002 }, \ 63 | beef = { 0.200, 0.100, -0.005, -0.005 }, \ 64 | mutton = { 0.150, 0.110, -0.003, -0.007 }, \ 65 | rice = { 0.000, 0.010, -0.100, -0.002 }, \ 66 | ["wheat bran"] = { 0.040, 0.010, -0.150, -0.008 }, \ 67 | gel = { 0.000, 0.000, -0.000, -0.000 }, \ 68 | }, \ 69 | } 70 | 71 | And finally, solve: 72 | 73 | --! continue 74 | primal, dual = rima.mp.solve(blending_problem, whiskas_data, { Q = 100 }) 75 | print(primal.objective) --> 0.52 76 | print(primal.f.chicken) --> 0 77 | print(primal.f.beef) --> 60 78 | print(primal.f.mutton) --> 0 79 | print(primal.f.rice) --> 0 80 | print(primal.f["wheat bran"]) --> 0 81 | print(primal.f.gel) --> 40 82 | 83 | [ [Contents](contents.html) | Previous: [Sums](sums.html) | Next: [Structures](structures.html) ] 84 | -------------------------------------------------------------------------------- /docs/functions.txt: -------------------------------------------------------------------------------- 1 | --! env rima = require("rima") io = require("io") 2 | 3 | # Rima Manual: Functions 4 | 5 | [ [Contents](contents.html) | Previous: [A Structured Knapsack](knapsack_2.html) | Next: [Cases](cases.html) ] 6 | 7 | You can define functions in Rima with the same syntax as sums: `rima.F{arg1, arg2,...}(expression)`. 8 | Functions are data in Rima, so they go in the table of data (or *scope*) that you pass into `rima.E`: 9 | 10 | f, x, y = rima.R"f, x, y" 11 | S = { f=rima.F{x, y}(y^x) } 12 | print(rima.E(f(2, 3), S)) --> 9 13 | 14 | The list of arguments can contain references or just strings. 15 | 16 | Functions "return" the result of evaluating their expression - which could well be an expression: 17 | 18 | f, x, y = rima.R"f, x, y" 19 | S = { f=rima.F{"y"}(rima.sin(y)) } 20 | print(rima.E(f(x), S)) --> sin(x) 21 | 22 | Function arguments can be expressions, 23 | and they'll get symbolic treatment if they're not defined: 24 | 25 | f, x, y = rima.R"f, x, y" 26 | S = { f=rima.F{y}(y*y) } 27 | print(rima.E(f(x^3), S)) --> x^6 28 | 29 | If a function uses a reference that doesn't have the same name as an argument, 30 | Rima will look look it up in the *global scope*. 31 | In this example, the `y` in `x * y` is not a function argument, 32 | and is found in the scope passed to `rima.E`: 33 | 34 | f, x, y, a = rima.R"f, x, y, a" 35 | F = rima.F{x}(x * y) 36 | print(rima.E(f(a), { f=F, a=2, y=3 })) --> 6 37 | 38 | You can define local variable for a function by passing them as the third argument to `rima.func` 39 | in the same manner that you pass global variables to `rima.E`. 40 | Here, `y` is neither defined as an argument or in the global scope, 41 | but in the function's local scope. 42 | 43 | f, x, y = rima.R"f, x, y" 44 | S = { f=rima.F{x}(x * y, { y=5 }) } 45 | print(rima.E(f(2), S)) --> 10 46 | 47 | When evaluating a function, 48 | Rima will look up references in the local scope before looking in the global scope. 49 | This means that if a local and global variable have the same names, 50 | Rima will do the right thing. 51 | Here, `F` has a local `y` defined as 5, while in the global scope, 52 | `y` is 100: 53 | 54 | f, x, y = rima.R"f, x, y" 55 | F = rima.F{x}(x * y, { y=5 }) 56 | print(rima.E(f(y), { f=F, y=100 })) --> 500 57 | 58 | The return value of an expression is another expression, 59 | and you can define any of the variables passed after you've evaluated the function: 60 | 61 | f, x, y, u, v = rima.R"f, x, y, u, v" 62 | F = rima.F{x}(x * y, { y=5 }) 63 | e = rima.E(u * f(v), { f=F }) 64 | print(e) --> 5*u*v 65 | print(rima.E(e, { u=2, v=3 })) --> 30 66 | 67 | You can call a Rima function like a regular Lua function. 68 | Keep in mind that the result might be an expression rather than a number: 69 | 70 | y = rima.R"y" 71 | F = rima.F{y}(y^2) 72 | print(F(5)) --> 25 73 | 74 | You can also call a Lua function as a Rima function. 75 | Be careful, because Rima will pass expressions to your Lua function, 76 | but then, this might not matter: 77 | 78 | f, x = rima.R"f, x" 79 | F = function(a) io.write("lua! ") return a + 1 end 80 | print(rima.E(2 * f(x), { f = F })) --> lua! 2*(1 + x) 81 | print(rima.E(2 * f(x), { f = F, x=3 })) --> lua! 8 82 | 83 | 84 | [ [Contents](contents.html) | Previous: [A Structured Knapsack](knapsack.html) | Next: [cases](cases.html) ] 85 | 86 | -------------------------------------------------------------------------------- /docs/references.txt: -------------------------------------------------------------------------------- 1 | --! env rima = require("rima") 2 | 3 | # Rima Manual: References 4 | 5 | [ [Contents](contents.html) | Previous: [Introduction to Lua](trouble.html) | Next: [Expressions](expressions.html) ] 6 | 7 | The values Rima works with, called *references*, are quite different from Lua variables, 8 | and probably quite different from variables and parameters you've seen in other modelling languages. 9 | 10 | For a start, Rima doesn't distinguish between variables 11 | (values Rima should solve for) 12 | and parameters 13 | (values that are inputs to the problem). 14 | Instead, when you ask Rima to solve a problem, 15 | it works out what it does and doesn't know, 16 | and if it can work out what it doesn't from what it does. 17 | 18 | Secondly, you can't directly assign a value to a reference. 19 | Instead, references are just the names you'll use to refer to your values, 20 | and the names you'll use to bind to data later. 21 | 22 | 23 | ## Defining References 24 | 25 | You define a reference with `rima.R`: 26 | 27 | x = rima.R"x" 28 | print(x) --> x 29 | 30 | `x` is now a Lua variable (so we can use it in Lua) that points to a Rima 31 | reference called `x`. 32 | You don't *have* to use the same name for the Lua variable and the reference, 33 | but if you don't you might get horribly confused: 34 | 35 | x = rima.R"y" 36 | print(x) --> y 37 | 38 | If you wish to define more than one variable at a time, you can: 39 | 40 | x, y = rima.R"x, y" 41 | 42 | If you're using variables in the global scope 43 | (we were above, and do for most of the manual), 44 | then `rima.define` offers an even easier way to define references. 45 | It automatically inserts the new references into the global scope: 46 | 47 | rima.define("x, y") 48 | print(x + y) --> x + y 49 | 50 | If, on they other hand, your references are going into a local scope, 51 | you must use `rima.R` - there's no way of inserting variables into a local scope. 52 | 53 | In the rest of the manual, we'll use `rima.R` and `rima.define` from time to time, 54 | the effect is the same, 55 | and you can choose which suits you. 56 | 57 | 58 | ## Evaluating References 59 | 60 | You can *evaluate* a reference with `rima.E`. 61 | We pass `rima.E` the reference we wish to evaluate, 62 | and a table of values to look the reference up in: 63 | 64 | x = rima.R"x" 65 | print(rima.E(x, { x=5 })) --> 5 66 | 67 | You can use the same reference in more than one evaluation, 68 | with different tables: 69 | 70 | rima.define"x" 71 | print(rima.E(x, { x=5 })) --> 5 72 | print(rima.E(x, { x="hello" })) --> hello 73 | 74 | Again, it's a good idea to give your variables and references the same name: 75 | 76 | x = rima.R"y" 77 | print(rima.E(x, { x="I'm x", y="I'm y" })) --> I'm y 78 | 79 | If you're a Lua user, it might help to think of rima.E as something like `setfenv` 80 | and references as global variables. 81 | If you're used to object oriented languages, 82 | it might help to thing of references as pointers to members, 83 | and the tables you pass in as objects. 84 | 85 | 86 | ## References to References 87 | 88 | It's quite ok for the table of values you pass in to contain other references: 89 | 90 | rima.define"x, y, z" 91 | print(rima.E(x, { x=y, y=z, z="you got to z!" })) --> you got to z! 92 | 93 | Next, we cover `rima.E` in more detail when building expressions. 94 | 95 | 96 | [ [Contents](contents.html) | Previous: [Introduction to Lua](trouble.html) | Next: [Expressions](expressions.html) ] 97 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | PACKAGE=rima 2 | VERSION=0.05 3 | 4 | LUA= $(shell echo `which lua`) 5 | LUA_BINDIR= $(shell echo `dirname $(LUA)`) 6 | LUA_PREFIX= $(shell echo `dirname $(LUA_BINDIR)`) 7 | LUA_SHAREDIR=$(LUA_PREFIX)/share/lua/5.1 8 | LUA_LIBDIR=$(LUA_PREFIX)/lib/lua/5.1 9 | LUA_INCDIR=$(LUA_PREFIX)/include 10 | 11 | COIN_PREFIX=/usr/local 12 | COIN_LIBDIR=$(COIN_PREFIX)/lib 13 | COIN_INCDIR=$(COIN_PREFIX)/include/coin 14 | 15 | LPSOLVE_PREFIX=/usr/local 16 | LPSOLVE_LIBDIR=$(LPSOLVE_PREFIX)/lib 17 | LPSOLVE_INCDIR=$(LPSOLVE_PREFIX)/include/lpsolve 18 | 19 | CPP=/usr/bin/g++ 20 | #-DNOMINMAX is needed for some compilers on windows. I'm not sure which, so I guess I'll just blanket-add it for now. Can't hurt, right? 21 | CFLAGS=-O3 -DNOMINMAX -fPIC 22 | SO_SUFFIX=so 23 | 24 | # Guess a platform 25 | UNAME=$(shell uname -s) 26 | ifneq (,$(findstring Darwin,$(UNAME))) 27 | # OS X 28 | # CFLAGS:=$(CFLAGS) -arch i686 -arch x86_64 # coin's really not set up for fat binaries 29 | SHARED=-bundle -undefined dynamic_lookup 30 | LIBS= 31 | else 32 | # Linux 33 | SHARED=-shared -llua 34 | LIBS=-lcstring 35 | endif 36 | 37 | 38 | all: clp cbc lpsolve 39 | 40 | clp: lua/rima_clp_core.$(SO_SUFFIX) 41 | 42 | cbc: lua/rima_cbc_core.$(SO_SUFFIX) 43 | 44 | lpsolve: lua/rima_lpsolve_core.$(SO_SUFFIX) 45 | 46 | ipopt: lua/rima_ipopt_core.$(SO_SUFFIX) 47 | 48 | lua/rima_clp_core.$(SO_SUFFIX): c/rima_clp_core.cpp c/rima_solver_tools.cpp 49 | $(CPP) $(CFLAGS) $(SHARED) $^ -o $@ -L$(COIN_LIBDIR) -lclp -lcoinutils -lcoinmumps -lcoinmetis -lbz2 -lz -framework vecLib $(LIBS) -I$(LUA_INCDIR) -I$(COIN_INCDIR) 50 | 51 | lua/rima_cbc_core.$(SO_SUFFIX): c/rima_cbc_core.cpp c/rima_solver_tools.cpp 52 | $(CPP) $(CFLAGS) $(SHARED) $^ -o $@ -L$(COIN_LIBDIR) -lcbc -losi -losiclp -lclp -lcgl -lcoinutils -lcoinmumps -lcoinmetis -framework vecLib $(LIBS) -I$(LUA_INCDIR) -I$(COIN_INCDIR) 53 | 54 | lua/rima_lpsolve_core.$(SO_SUFFIX): c/rima_lpsolve_core.cpp c/rima_solver_tools.cpp 55 | $(CPP) $(CFLAGS) $(SHARED) $^ -o $@ -L$(LPSOLVE_LIBDIR) -llpsolve55 $(LIBS) -I$(LUA_INCDIR) -I$(LPSOLVE_INCDIR) 56 | 57 | lua/rima_ipopt_core.$(SO_SUFFIX): c/rima_ipopt_core.cpp 58 | $(CPP) $(CFLAGS) $(SHARED) $^ -o $@ -L$(COIN_LIBDIR) -lipopt -lcoinmumps -lcoinmetis -lgfortran -framework vecLib $(LIBS) -I$(LUA_INCDIR) -I$(COIN_INCDIR) 59 | 60 | test: all lua/rima.lua 61 | cd lua; $(LUA) rima-test.lua; $(LUA) rima-test-solvers.lua 62 | cd lua; for f in `find ../docs -name "*.txt"`; do $(LUA) test/doctest.lua -i $$f > /dev/null; done 63 | 64 | install: lua/rima.lua 65 | mkdir -p $(LUA_SHAREDIR) 66 | mkdir -p $(LUA_LIBDIR) 67 | cp lua/rima.lua $(LUA_SHAREDIR) 68 | cp -r lua/rima $(LUA_SHAREDIR) 69 | -cp lua/rima_*_core.so $(LUA_LIBDIR) 70 | 71 | uninstall: 72 | rm -f $(LUA_SHAREDIR)/rima.lua 73 | rm -rf $(LUA_SHAREDIR)/rima 74 | -rm $(LUA_LIBDIR)/rima_*_core.so 75 | 76 | doc: lua/rima.lua 77 | cd lua; for f in `find ../docs -name "*.txt"`; do n=`basename $$f .txt`; $(LUA) test/doctest.lua -sh -i $$f | markdown.lua -e ../docs/header.html -f ../docs/footer.html > ../htmldocs/$$n.html; done 78 | 79 | dist: doc 80 | rm -f dist.files 81 | rm -rf $(PACKAGE)-$(VERSION) 82 | rm -f $(PACKAGE)-$(VERSION).tar.gz 83 | find * | grep -v "\.svn" | grep -v "^\.DS_Store$$" | grep -v "^build$$" | grep -v "\.so$$" | grep -v "\.o$$" | grep -v "\.git" | grep -v "\.rockspec" | grep -v "update-copyright.sh" > dist.files 84 | mkdir -p $(PACKAGE)-$(VERSION) 85 | cpio -p $(PACKAGE)-$(VERSION) < dist.files 86 | tar czvf $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE)-$(VERSION) 87 | rm -f dist.files 88 | rm -rf $(PACKAGE)-$(VERSION) 89 | 90 | clean: 91 | rm -f lua/rima_*_core.so 92 | rm -f htmldocs/*.html 93 | rm -f $(PACKAGE)-$(VERSION).tar.gz 94 | rm -f lua/luacov.*.out 95 | 96 | -------------------------------------------------------------------------------- /lua/tests/rima/index.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local index = require("rima.index") 5 | 6 | local object = require("rima.lib.object") 7 | local interface = require("rima.interface") 8 | 9 | 10 | ------------------------------------------------------------------------------ 11 | 12 | return function(T) 13 | local N = interface.new_index 14 | local S = interface.set_index 15 | local R = interface.R 16 | local E = interface.eval 17 | 18 | -- constructors 19 | T:test(object.typeinfo(index:new()).index, "typeinfo(index:new()).index") 20 | T:test(object.typename(index:new(), "index"), "typename(index:new())=='index'") 21 | -- T:expect_error(function() N(B, 1) end, "the first element of this index must be an identifier string") 22 | T:expect_ok(function() N({}, 1) end) 23 | 24 | -- identifiers 25 | -- T:test(index.is_identifier(N(R, "a"))) 26 | -- T:test(not index.is_identifier(N(R, "a", "b"))) 27 | -- T:test(not index.is_identifier(N({}, "a"))) 28 | 29 | -- indexing 30 | T:check_equal(N(nil, "a", "b"), "a.b") 31 | T:check_equal(N(nil, "a", 1), "a[1]") 32 | 33 | -- resolving 34 | T:check_equal(E(N({a=1}, "a")), 1) 35 | T:check_equal(E(N(nil, "a"), {a=3}), 3) 36 | 37 | -- setting 38 | do 39 | local S = {} 40 | local I = N(S) 41 | T:expect_ok(function() I.a = 1 end) 42 | T:check_equal(E(I.a), 1) 43 | T:check_equal(E(N(nil, "a"), S), 1) 44 | T:expect_ok(function() I.b.c.d = 3 end) 45 | T:check_equal(E(I.b.c.d), 3) 46 | T:check_equal(E(N().b.c.d, S), 3) 47 | T:check_equal(E(N().f.g.h, S), "f.g.h") 48 | 49 | local I2 = {a=5, b={c={d=7}}} 50 | T:check_equal(E(N(I2).a, S), 5) 51 | T:check_equal(E(N(I2).b.c.d, S), 7) 52 | 53 | T:expect_error(function() N().a.b = 1 end, "%L: error setting 'a.b' to '1': 'a.b' isn't bound to a table or scope") 54 | 55 | -- errors 56 | T:expect_error(function() local dummy = E(I.a.b) end, "%L: error indexing 'a' as 'a%.b': can't index a number") 57 | T:expect_error(function() I.a.b = 1 end, "%L: error indexing 'a' as 'a%.b': can't index a number") 58 | T:expect_error(function() I.a.b.c = 1 end, "%L: error indexing 'a%' as 'a%.b%': can't index a number") 59 | end 60 | 61 | -- variable indexes 62 | local I3 = { a={b=5} } 63 | local i = N().i 64 | T:check_equal(N().a, "a") 65 | T:check_equal(N().a.b, "a.b") 66 | T:check_equal(N().a[i], "a[i]") 67 | T:check_equal(E(N().a[i], I3), "a[i]") 68 | I3.i = "b" 69 | T:check_equal(E(N().a[i], I3), 5) 70 | 71 | -- table assign 72 | local t = {} 73 | local I = N(t) 74 | T:expect_ok(function() I.a = { x=1, y=2 } end) 75 | T:check_equal(t.a.x, 1) 76 | T:check_equal(t.a.y, 2) 77 | 78 | -- set 79 | local t = {} 80 | local I = N() 81 | T:expect_ok(function() S(I.b, t, 7) end) 82 | T:expect_ok(function() S(I.a, t, { x=1, y=2 }) end) 83 | T:check_equal(t.b, 7) 84 | T:check_equal(t.a.x, 1) 85 | T:check_equal(t.a.y, 2) 86 | 87 | -- references to indexes 88 | local t = { a={b=N().c.d}, c={d={e=N().f.g}}, f={g={h=N().i.j}} } 89 | T:check_equal(E(N().a.b.z, t), "c.d.z") 90 | T:check_equal(E(N().a.b.e.z, t), "f.g.z") 91 | T:check_equal(E(N().a.b.e.h, t), "i.j") 92 | T:check_equal(E(N().a.b.e.h.k, t), "i.j.k") 93 | t.i = {j={k=7}} 94 | T:check_equal(E(N().a.b.e.h.k, t), 7) 95 | 96 | -- index introspection 97 | local list = {} 98 | local i = N(nil, "a", 1, "b") 99 | index.__list_variables(i, {}, list) 100 | T:check_equal(list["a[1].b"].ref, "a[1].b") 101 | end 102 | 103 | 104 | ------------------------------------------------------------------------------ 105 | 106 | -------------------------------------------------------------------------------- /lua/rima/operators/add_mul.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local lib = require("rima.lib") 5 | local core = require("rima.core") 6 | 7 | local add_mul = {} 8 | 9 | 10 | ------------------------------------------------------------------------------ 11 | 12 | function add_mul.evaluate_terms(terms, ...) 13 | local new_terms 14 | local term_count = #terms 15 | for i = 1, term_count do 16 | local t = terms[i] 17 | local et2 = core.eval(t[2], ...) 18 | if et2 ~= t[2] then 19 | new_terms = new_terms or {} 20 | new_terms[i] = { t[1], et2 } 21 | end 22 | end 23 | if new_terms then 24 | for i = 1, term_count do 25 | if not new_terms[i] then 26 | new_terms[i] = terms[i] 27 | end 28 | end 29 | end 30 | 31 | return new_terms 32 | end 33 | 34 | 35 | ------------------------------------------------------------------------------ 36 | 37 | local SCOPE_FORMAT = { scopes = true } 38 | 39 | function add_mul.add_term(terms, term_map, coeff, e, id, sort) 40 | local id = id or lib.repr(e, SCOPE_FORMAT) 41 | local t = term_map[id] 42 | if coeff == 0 then 43 | return true -- Do nothing, but either way we removed a term 44 | end 45 | if t then 46 | t[1] = t[1] + coeff 47 | return true 48 | else 49 | local new_term = { coeff, e, id=id, sort=sort or lib.repr(e) } 50 | terms[#terms+1] = new_term 51 | term_map[id] = new_term 52 | end 53 | end 54 | 55 | 56 | ------------------------------------------------------------------------------ 57 | 58 | local function term_order(a, b) 59 | return a.sort < b.sort 60 | end 61 | 62 | function add_mul.sort_terms(terms) 63 | -- sort the new terms alphabetically, so that when we group by a string 64 | -- representation, like terms look alike 65 | local term_count = 0 66 | local prev, need_sort 67 | for i = 1, #terms do 68 | local t = terms[i] 69 | terms[i] = nil 70 | if t[1] ~= 0 then 71 | if t[2] == " " then 72 | t[1], t[2] = 1, t[1] 73 | end 74 | term_count = term_count + 1 75 | terms[term_count] = t 76 | if prev and prev.sort > t.sort then 77 | need_sort = true 78 | end 79 | prev = t 80 | end 81 | end 82 | if need_sort then 83 | table.sort(terms, term_order) 84 | end 85 | return term_count, need_sort 86 | end 87 | 88 | 89 | ------------------------------------------------------------------------------ 90 | 91 | function add_mul.extract_constant(e) 92 | local constant = e[1][2] 93 | 94 | if type(constant) == "number" then 95 | local term_count = #e 96 | 97 | if term_count == 1 then 98 | return constant -- There was just a constant 99 | end 100 | 101 | if term_count == 2 and e[2][1] == 1 then 102 | -- there's a constant and only one other argument with a 103 | -- coefficient/exponent of 1 - hoist the other argument 104 | return constant, e[2][2] 105 | end 106 | 107 | local new_terms = {} 108 | for i = 2, term_count do 109 | new_terms[i-1] = e[i] 110 | end 111 | return constant, getmetatable(e):new(new_terms) 112 | end 113 | end 114 | 115 | 116 | ------------------------------------------------------------------------------ 117 | 118 | function add_mul.list_variables(op, S, list) 119 | for i = 1, #op do 120 | core.list_variables(op[i][2], S, list) 121 | end 122 | end 123 | 124 | 125 | ------------------------------------------------------------------------------ 126 | 127 | return add_mul 128 | 129 | ------------------------------------------------------------------------------ 130 | 131 | -------------------------------------------------------------------------------- /htmldocs/scripts/shBrushLua.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SyntaxHighlighter 3 | * http://alexgorbatchev.com/ 4 | * 5 | * SyntaxHighlighter is donationware. If you are using it, please donate. 6 | * http://alexgorbatchev.com/wiki/SyntaxHighlighter:Donate 7 | * 8 | * @version 9 | * 2.0.320 (May 03 2009) 10 | * 11 | * @copyright 12 | * Copyright (C) 2004-2009 Alex Gorbatchev. 13 | * 14 | * @license 15 | * This file is part of SyntaxHighlighter. 16 | * 17 | * SyntaxHighlighter is free software: you can redistribute it and/or modify 18 | * it under the terms of the GNU Lesser General Public License as published by 19 | * the Free Software Foundation, either version 3 of the License, or 20 | * (at your option) any later version. 21 | * 22 | * SyntaxHighlighter is distributed in the hope that it will be useful, 23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 | * GNU General Public License for more details. 26 | * 27 | * You should have received a copy of the GNU General Public License 28 | * along with SyntaxHighlighter. If not, see . 29 | */ 30 | SyntaxHighlighter.brushes.Lua = function() 31 | { 32 | var keywords = 'and break do else elseif ' + 33 | 'end false for function if ' + 34 | 'in local nil not or ' + 35 | 'repeat return then true until while'; 36 | 37 | var functions = 'assert collectgarbage dofile error getfenv ' + 38 | 'getmetatable ipairs load loadfile loadstring ' + 39 | 'next pairs pcall print rawequal ' + 40 | 'rawget rawset select setfenv setmetatable ' + 41 | 'tonumber tostring type unpack xpcall'; 42 | 43 | this.regexList = [ 44 | { regex: new RegExp('[0-9][\\.0-9]*', 'gm'), css: 'constants' }, // -- comments 45 | { regex: new RegExp('[{}=\\[\\]]', 'gm'), css: 'color1' }, // -- comments 46 | { regex: new RegExp('--[^!\\[].*$', 'gm'), css: 'comments' }, // -- comments 47 | { regex: new RegExp('--\\[\\[[\\s\\S]*?\\]\\]', 'gm'), css: 'comments' }, // [[...]] comments 48 | { regex: new RegExp('--\\[=\\[[\\s\\S]*?\\]=\\]', 'gm'), css: 'comments' }, // [==[.. comments 49 | { regex: new RegExp('--\\[==\\[[\\s\\S]*?\\]==\\]', 'gm'), css: 'comments' }, // [==[.. comments 50 | { regex: new RegExp('--\\[===\\[[\\s\\S]*?\\]===\\]', 'gm'), css: 'comments' }, // [===[.. comments 51 | { regex: new RegExp('--\\[====\\[[\\s\\S]*?\\]====\\]', 'gm'), css: 'comments' }, // [===[.. comments 52 | { regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // double quoted strings 53 | { regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // single quoted strings 54 | { regex: new RegExp('\\[\\[[\\s\\S]*?\\]\\]', 'gm'), css: 'string' }, // [[...]] string 55 | { regex: new RegExp('\\[=\\[[\\s\\S]*?\\]=\\]', 'gm'), css: 'string' }, // [=[...]=] string 56 | { regex: new RegExp('\\[==\\[[\\s\\S]*?\\]==\\]', 'gm'), css: 'string' }, // [==[...]===] string 57 | { regex: new RegExp('\\[===\\[[\\s\\S]*?\\]===\\]', 'gm'), css: 'string' }, // [===[...]===] string 58 | { regex: new RegExp(this.getKeywords(functions), 'gm'), css: 'functions bold' }, 59 | { regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' }, // keywords 60 | { regex: new RegExp(this.getKeywords('rima\\.[\\w\\.]+'), 'gm'), css: 'color1' } // rima identifiers 61 | ]; 62 | 63 | this.forHtmlScript(SyntaxHighlighter.regexLib.scriptScriptTags); 64 | }; 65 | 66 | SyntaxHighlighter.brushes.Lua.prototype = new SyntaxHighlighter.Highlighter(); 67 | SyntaxHighlighter.brushes.Lua.aliases = ['lua']; 68 | -------------------------------------------------------------------------------- /lua/rima/operators/sum.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local object = require("rima.lib.object") 5 | local operator = require("rima.operator") 6 | local lib = require("rima.lib") 7 | local core = require("rima.core") 8 | local closure = require("rima.closure") 9 | local add = require("rima.operators.add") 10 | local ops = require("rima.operations") 11 | local set_list = require("rima.sets.list") 12 | 13 | 14 | ------------------------------------------------------------------------------ 15 | 16 | local sum = operator:new_class({}, "sum") 17 | sum.precedence = 1 18 | 19 | 20 | function sum:simplify() 21 | local ti = object.typeinfo(self[1]) 22 | if ti.closure then 23 | return self 24 | elseif ti["sets.list"] then 25 | return sum:new{ closure:new(self[2], self[1]) } 26 | else 27 | local term_count = #self 28 | local sets = set_list:read(self, term_count-1) 29 | return sum:new{ closure:new(self[term_count], sets) } 30 | end 31 | end 32 | 33 | 34 | ------------------------------------------------------------------------------ 35 | 36 | local formats = 37 | { 38 | dump = "sum(%s)", 39 | lua = "rima.sum%s", 40 | latex = "\\sum_%s", 41 | other = "sum%s" 42 | } 43 | 44 | function sum:__repr(format) 45 | local f = formats[format.format] or formats.other 46 | return f:format(lib.repr(self[1], format)) 47 | end 48 | 49 | 50 | ------------------------------------------------------------------------------ 51 | 52 | function sum:__eval(S) 53 | local cl = self[1] 54 | 55 | -- Iterate through all the elements of the sets, collecting defined and 56 | -- undefined terms 57 | local defined_terms, undefined_terms = {}, {} 58 | for S2, undefined in cl:iterate(S) do 59 | local z = core.eval(ops.add(0, cl.exp), S2) -- the +0 helps to "cast" e to a number (if it's a set element) 60 | if undefined and undefined[1] then 61 | -- Undefined terms are stored in groups based on the undefined sum 62 | -- indices (so we can group them back into sums over the same indices) 63 | local name = lib.concat(undefined, ",", lib.repr) 64 | local terms 65 | local udn = undefined_terms[name] 66 | if not udn then 67 | terms = {} 68 | undefined_terms[name] = { iterators=undefined, terms=terms } 69 | else 70 | terms = udn.terms 71 | end 72 | terms[#terms+1] = { 1, z } 73 | else 74 | -- Defined terms are just stored in a list 75 | defined_terms[#defined_terms+1] = { 1, z } 76 | end 77 | end 78 | 79 | -- Run through all the undefined terms, rebuilding the sums 80 | local total_terms = {} 81 | for n, t in pairs(undefined_terms) do 82 | local z 83 | if #t.terms > 1 then 84 | z = add:new(t.terms) 85 | else 86 | z = t.terms[1][2] 87 | end 88 | total_terms[#total_terms+1] = {1, sum:new{ t.iterators, cl:undo(z, t.iterators) }} 89 | end 90 | 91 | -- Add the defined terms onto the end 92 | for _, t in ipairs(defined_terms) do 93 | total_terms[#total_terms+1] = t 94 | end 95 | 96 | if #total_terms == 1 then 97 | return total_terms[1][2] 98 | else 99 | return core.eval(add:new(total_terms), S) 100 | end 101 | end 102 | 103 | 104 | ------------------------------------------------------------------------------ 105 | 106 | function sum:__list_variables(S, list) 107 | local cl = self[1] 108 | for S2, undefined in cl:iterate(S) do 109 | local S3 = cl:fake_iterate(S2, undefined) 110 | core.list_variables(core.eval(cl, S3), S3, list) 111 | end 112 | end 113 | 114 | 115 | ------------------------------------------------------------------------------ 116 | 117 | return sum 118 | 119 | ------------------------------------------------------------------------------ 120 | 121 | -------------------------------------------------------------------------------- /lua/rima/operators/product.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local object = require("rima.lib.object") 5 | local operator = require("rima.operator") 6 | local lib = require("rima.lib") 7 | local core = require("rima.core") 8 | local closure = require("rima.closure") 9 | local mul = require("rima.operators.mul") 10 | local set_list = require("rima.sets.list") 11 | local ops = require("rima.operations") 12 | 13 | 14 | ------------------------------------------------------------------------------ 15 | 16 | local product = operator:new_class({}, "product") 17 | product.precedence = 1 18 | 19 | 20 | function product:simplify() 21 | local ti = object.typeinfo(self[1]) 22 | if ti.closure then 23 | return self 24 | elseif ti["sets.list"] then 25 | return product:new{ closure:new(self[1], self[2]) } 26 | else 27 | local term_count = #self 28 | local sets = set_list:read(self, term_count-1) 29 | return product:new{ closure:new(self[term_count], sets) } 30 | end 31 | end 32 | 33 | 34 | ------------------------------------------------------------------------------ 35 | 36 | local formats = 37 | { 38 | dump = "product(%s)", 39 | lua = "rima.product%s", 40 | latex = "\\prod_%s", 41 | other = "prod%s" 42 | } 43 | 44 | function product:__repr(format) 45 | local f = formats[format.format] or formats.other 46 | return f:format(lib.repr(self[1], format)) 47 | end 48 | 49 | 50 | ------------------------------------------------------------------------------ 51 | 52 | function product:__eval(S) 53 | local cl = self[1] 54 | 55 | -- Iterate through all the elements of the sets, collecting defined and 56 | -- undefined terms 57 | local defined_terms, undefined_terms = {}, {} 58 | for S2, undefined in cl:iterate(S) do 59 | local z = core.eval(ops.add(0, cl.exp), S2) -- the +0 helps to "cast" e to a number (if it's a set element) 60 | if undefined and undefined[1] then 61 | -- Undefined terms are stored in groups based on the undefined product 62 | -- indices (so we can group them back into products over the same indices) 63 | local name = lib.concat(undefined, ",", lib.repr) 64 | local terms 65 | local udn = undefined_terms[name] 66 | if not udn then 67 | terms = {} 68 | undefined_terms[name] = { iterators=undefined, terms=terms } 69 | else 70 | terms = udn.terms 71 | end 72 | terms[#terms+1] = { 1, z } 73 | else 74 | -- Defined terms are just stored in a list 75 | defined_terms[#defined_terms+1] = { 1, z } 76 | end 77 | end 78 | 79 | -- Run through all the undefined terms, rebuilding the products 80 | local total_terms = {} 81 | for n, t in pairs(undefined_terms) do 82 | local z 83 | if #t.terms > 1 then 84 | z = mul:new(t.terms) 85 | else 86 | z = t.terms[1][2] 87 | end 88 | total_terms[#total_terms+1] = {1, product:new{ t.iterators, cl:undo(z, t.iterators) } } 89 | end 90 | 91 | -- Add the defined terms onto the end 92 | for _, t in ipairs(defined_terms) do 93 | total_terms[#total_terms+1] = t 94 | end 95 | 96 | if #total_terms == 1 then 97 | return total_terms[1][2] 98 | else 99 | return core.eval(mul:new(total_terms), S) 100 | end 101 | end 102 | 103 | 104 | ------------------------------------------------------------------------------ 105 | 106 | function product:__list_variables(S, list) 107 | local cl = self[1] 108 | for S2, undefined in cl:iterate(S) do 109 | local S3 = cl:fake_iterate(S2, undefined) 110 | core.list_variables(core.eval(cl, S3), S3, list) 111 | end 112 | end 113 | 114 | 115 | ------------------------------------------------------------------------------ 116 | 117 | return product 118 | 119 | ------------------------------------------------------------------------------ 120 | 121 | -------------------------------------------------------------------------------- /docs/install.txt: -------------------------------------------------------------------------------- 1 | # Installing Rima 2 | 3 | Rima is open source, licensed under the [MIT Licence](http://www.opensource.org/licenses/mit-license.php). 4 | You can download the latest version of Rima [here](https://github.com/downloads/geoffleyland/rima/rima-latest.tar.gz), 5 | or visit Rima's project page on [GitHub](https://github.com/geoffleyland/rima). 6 | 7 | In order to get Rima running, first you'll need Lua, 8 | and a linear program solver. 9 | 10 | ## Prerequisites 11 | 12 | ### Lua 13 | 14 | You'll need to have Lua 5.1 installed and working (which should be easy). 15 | Instructions and binaries are [here](http://www.lua.org/download.html). 16 | Building Lua from source is very easy (possibly easier than using a package manager), 17 | but there probably is a package for your platform: 18 | 19 | + Linux: There's probably a package for your distribution that you can install with apt-get or yum. 20 | Make sure you get Lua 5.1 (not 5.0) and that you get the headers - you'll probably need to install a development package 21 | + OS X: if have [MacPorts](http://www.macports.org/) installed you can `port install lua` (there must be a similar command for [Fink](http://www.finkproject.org/)) 22 | + Windows: there's [Lua for Windows](http://luaforwindows.luaforge.net/) which is a binary installer for Lua and a range of modules 23 | 24 | You might want to get LuaJIT instead, which is considerably faster on x86 platforms. 25 | It's available [here](http://luajit.org/) 26 | 27 | ### Solvers 28 | 29 | If you want to solve any optimisation problems 30 | (you could just play with Rima's late-bound symbolic maths stuff, but I don't think it'll keep you amused for long), 31 | you'll need to install one of the solvers. 32 | 33 | [lpsolve](http://sourceforge.net/projects/lpsolve) is probably easier to get running - it compiles to one shared library. 34 | The download contains makefiles, build scripts and project files for most platforms. 35 | You'll need version 5.5. 36 | 37 | The COIN solvers take a bit more work, and build several shared libraries. 38 | I think the right way to install them is to check out the latest stable version of 39 | [CBC](https://projects.coin-or.org/Cbc/) 40 | from COIN's [Subversion server](https://projects.coin-or.org/svn/Cbc/stable/) and proceed from there. 41 | I took a bit of a more tortuous route, and use the COIN trunk. 42 | 43 | Rima also solves nonlinear problems if you have [ipopt](https://projects.coin-or.org/Ipopt) installed. 44 | 45 | ## Building and installing Rima 46 | 47 | There's a makefile in the distribution which, at this stage, I'm afraid you'll almost certainly have to modify yourself. 48 | It works for me on OS X, and I'd appreciate it if you let me know what works on your setup. 49 | 50 | If you have CLP, CBC and lpsolve installed, ideally, you'll just have to: 51 | 52 | --! ignore 53 | make 54 | make test 55 | sudo make install 56 | 57 | If you have ipopt installed then you'll need to build it separately: 58 | --! ignore 59 | make ipopt 60 | 61 | 62 | ### Building 63 | 64 | Unfortunately your solvers might not be where the makefile expects them, 65 | and you'll have to edit the makefile, modifying the lines 66 | 67 | --! ignore 68 | COIN_PREFIX=/usr/local 69 | 70 | and 71 | 72 | --! ignore 73 | LPSOLVE_PREFIX=/usr/local 74 | 75 | or alternatively, `make COIN_PREFIX=/path/to/coin`. 76 | 77 | If you don't have all the solvers (or any), just make the ones you want: `make clp cbc COIN_PREFIX=/path/to/coin` 78 | 79 | ### Testing 80 | 81 | To test Rima, you'll need the Lua Filesystem library from [here](http://keplerproject.github.com/luafilesystem/). 82 | Then run `make test`. 83 | 84 | ### Installing 85 | 86 | `sudo make install` should install Rima and any solver bindings you've built to the appropriate locations 87 | (where Lua can find them). 88 | 89 | ### Making Documentation 90 | 91 | To build the documentation (`make doc`), 92 | you'll need to install [markdown.lua](http://www.frykholm.se/files/markdown.lua) first. 93 | 94 | -------------------------------------------------------------------------------- /lua/test/series.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | 5 | -- Test Tools ---------------------------------------------------------------- 6 | 7 | local series = {} 8 | 9 | function series:new(options, name) 10 | if not options.quiet then 11 | io.write(("Testing %s...\n"):format(name)) 12 | end 13 | 14 | self.__index = self 15 | return setmetatable( 16 | { name = name, options = options, tests = 0, fails = 0 }, self) 17 | end 18 | 19 | 20 | function series:close() 21 | if not self.options.quiet then 22 | io.write(("%s: %s - passed %d/%d tests\n"):format( 23 | self.name, self.fails == 0 and "pass" or "*****FAIL*****", 24 | self.tests - self.fails, self.tests)) 25 | end 26 | return self.fails == 0, self.tests, self.fails 27 | end 28 | 29 | 30 | local function test_source_line(depth) 31 | local info = debug.getinfo(depth, "Sl") 32 | return ("%s:%d"):format(info.short_src, info.currentline) 33 | end 34 | 35 | function series:test(pass, description, message, depth) 36 | depth = depth or 2 37 | self.tests = self.tests + 1 38 | if not pass and not self.options.dont_show_fails then 39 | io.write(("%s test, %s%s: *****FAIL*****%s\n"):format( 40 | self.name, test_source_line(depth+1), 41 | description and (" (%s)"):format(description) or "", 42 | message and (": %s"):format(message) or "")) 43 | self.fails = self.fails + 1 44 | elseif self.options.show_passes then 45 | io.write(("%s test, %s%s: pass%s\n"):format( 46 | self.name, test_source_line(depth+1), 47 | description and (" (%s)"):format(description) or "", 48 | message and (": %s"):format(message) or "")) 49 | end 50 | return pass 51 | end 52 | 53 | 54 | function series:check_equal(got, expected, description, depth) 55 | got, expected = tostring(got), tostring(expected) 56 | local pass = got == expected 57 | return self:test(pass, description, 58 | pass and ("got expected string \"%s\""):format(got) or 59 | ("result mismatch:\n expected: \"%s\"\n got: \"%s\""):format( 60 | expected:gsub("\n", "\n "), 61 | got:gsub("\n", "\n ")), (depth or 0) + 3), 1 62 | -- The ", 1" above (and on following lines) is to prevent tail calls, which 63 | -- can mess up the stack levels reported by LuaJIT 64 | end 65 | 66 | 67 | function series:expect_ok(f, description, depth) 68 | local status, message = xpcall(f, debug.traceback) 69 | return self:test(status, description, not status and 70 | ("unexpected error%s"):format( 71 | message and (" \"%s\""):format(message) or ""), (depth or 0) + 3), 1 72 | end 73 | 74 | 75 | function series:expect_error(f, expected, description, depth) 76 | depth = (depth or 0) + 3 77 | expected = expected:gsub("%%L", test_source_line(depth):gsub("%.", "%.")) 78 | 79 | local status, message = xpcall(f, debug.traceback) 80 | 81 | if status then 82 | return self:test(false, description, 83 | ("got ok, expected error:\n \"%s\""):format( 84 | expected:gsub("\n", "\n ")), depth), 1 85 | elseif not message:match(expected) then 86 | return self:test(false, description, 87 | ("expected error:\n \"%s\"\ngot error:\n \"%s\""):format( 88 | expected:gsub("\n", "\n "), message:gsub("\n", "\n ")), depth), 1 89 | else 90 | return self:test(true, description, 91 | ("got expected error:\n \"%s\""):format( 92 | message:gsub("\n", "\n ")), depth), 1 93 | end 94 | end 95 | 96 | 97 | function series:run(f, path) 98 | local T = self:new(self.options, path) 99 | f(T) 100 | local _, tests, fails = T:close() 101 | 102 | self.tests = self.tests + tests 103 | self.fails = self.fails + fails 104 | end 105 | 106 | 107 | ------------------------------------------------------------------------------ 108 | 109 | return series 110 | 111 | 112 | ------------------------------------------------------------------------------ 113 | 114 | -------------------------------------------------------------------------------- /lua/examples/sudoku.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | require("rima") 5 | 6 | -------------------------------------------------------------------------------- 7 | 8 | --[[ 9 | This is the rima version of the sudoku problem. 10 | The PuLP version is at http://code.google.com/p/pulp-or/source/browse/trunk/pulp-or/examples/Sudoku1.py 11 | and a python-zibopt version at http://code.google.com/p/python-zibopt/source/browse/trunk/examples/sudoku.py 12 | --]] 13 | 14 | r, rows = rima.R"r, rows" 15 | c, columns = rima.R"c, columns" 16 | v, values = rima.R"v, values" 17 | g1, g2, groups = rima.R"g1, g2, groups" -- groups for the little 3x3 blocks 18 | answer = rima.R"answer" 19 | initial = rima.R"initial" -- the starting solution 20 | 21 | -- Create the model and set up the constraints 22 | sudoku = rima.mp.new() 23 | sudoku.one_value_per_cell[{r=rows}][{c=columns}] = rima.mp.C(rima.sum{v=values}(answer[r][c][v]), "==", 1) 24 | sudoku.each_value_once_per_row[{r=rows}][{v=values}] = rima.mp.C(rima.sum{c=columns}(answer[r][c][v]), "==", 1) 25 | sudoku.each_value_once_per_column[{c=columns}][{v=values}] = rima.mp.C(rima.sum{r=rows}(answer[r][c][v]), "==", 1) 26 | sudoku.each_value_once_per_square[{g1=groups}][{g2=groups}][{v=values}] = rima.mp.C(rima.sum{r=rima.range(g1, g1+2)}{c=rima.range(g2, g2+2)}(answer[r][c][v]), "==", 1) 27 | 28 | -- We don't have an objective, we just want a feasible solution 29 | sudoku.objective = 1 30 | sudoku.sense = "minimise" 31 | 32 | 33 | -- Write out our model 34 | -- (this doesn't work because the variable tracer gets confused) 35 | --io.write("Sudoku model:\n", tostring(sudoku), "\n") 36 | --[[ 37 | Minimise: 38 | 1 39 | Subject to: 40 | one_value_per_cell[r in rows, c in columns]: sum{v in values}(answer[r, c, v]) == 1 41 | each_value_once_per_column[c in columns, v in values]: sum{r in rows}(answer[r, c, v]) == 1 42 | each_value_once_per_row[r in rows, v in values]: sum{c in columns}(answer[r, c, v]) == 1 43 | each_value_once_per_square[g1 in groups, g2 in groups, v in values]: sum{c in range(g2, 2 + g2)}{r in range(g1, 2 + g1)}(answer[r, c, v]) == 1 44 | --]] 45 | 46 | -- We use the initial solution to decide whether each of the elements of answer 47 | -- is a variable or a constant. 48 | sudoku.answer[{r=rows}][{c=columns}][{v=values}] = rima.case(initial[r][c], 49 | { 50 | { v, 1 }, 51 | { 0, rima.binary() }, 52 | }, 0) 53 | 54 | 55 | -- Set up a 9 by 9 sudoku problem 56 | sudoku_9_by_9 = rima.mp.new(sudoku, 57 | { 58 | rows = rima.range(1, 9), 59 | columns = rima.range(1, 9), 60 | values = rima.range(1, 9), 61 | groups = {1, 4, 7}, 62 | }) 63 | 64 | 65 | -- The example sudoku grid from zibopt 66 | zibopt_data = 67 | { 68 | {0, 0, 0, 6, 9, 2, 0, 4, 0}, 69 | {7, 0, 0, 0, 0, 0, 8, 9, 0}, 70 | {0, 0, 0, 0, 0, 0, 0, 0, 6}, 71 | 72 | {0, 0, 9, 0, 1, 7, 0, 0, 3}, 73 | {0, 0, 7, 0, 8, 0, 5, 0, 0}, 74 | {8, 0, 0, 4, 6, 0, 1, 0, 0}, 75 | 76 | {5, 0, 0, 0, 0, 0, 0, 0, 0}, 77 | {0, 8, 6, 0, 0, 0, 0, 0, 1}, 78 | {0, 3, 0, 7, 2, 8, 0, 0, 0} 79 | } 80 | 81 | -- Solve the problem 82 | local primal, dual = rima.mp.solve(sudoku_9_by_9, { initial = zibopt_data }) 83 | 84 | -- Print the answer nicely. There's probably an easier way to do this. 85 | if primal then 86 | io.write("\nSudoku answer\n") 87 | 88 | for i, v in pairs(primal.answer) do 89 | for j, w in pairs(v) do 90 | for k, x in pairs(w) do 91 | if x == 1 then zibopt_data[i][j] = k end 92 | end 93 | end 94 | end 95 | 96 | for i, r in ipairs(zibopt_data) do 97 | for j, v in ipairs(r) do 98 | io.write(("%d "):format(v)) 99 | if j == 3 or j == 6 then io.write(" ") end 100 | end 101 | io.write("\n") 102 | if i == 3 or i == 6 then io.write("\n") end 103 | end 104 | end 105 | 106 | 107 | -- EOF ------------------------------------------------------------------------- 108 | 109 | -------------------------------------------------------------------------------- /lua/rima/sets/list.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local coroutine, table = require("coroutine"), require("table") 5 | local error, ipairs, pairs, pcall, require, type = 6 | error, ipairs, pairs, pcall, require, type 7 | 8 | local object = require("rima.lib.object") 9 | local lib = require("rima.lib") 10 | local core = require("rima.core") 11 | local index = require("rima.index") 12 | 13 | module(...) 14 | 15 | local ref = require("rima.sets.ref") 16 | local scope = require("rima.scope") 17 | 18 | 19 | -- Set list -------------------------------------------------------------------- 20 | 21 | list = object:new_class(_M, "sets.list") 22 | 23 | 24 | function list:new(l) 25 | return object.new(self, l or {}) 26 | end 27 | 28 | 29 | function list.copy(sets) 30 | local new_list = {} 31 | for i, s in ipairs(sets) do new_list[i] = s end 32 | return list:new(new_list) 33 | end 34 | 35 | 36 | function list:read(sets, count) 37 | if not sets or count == 0 then return object.new(self, {}) end 38 | 39 | local new_sets = {} 40 | for i = 1, count do 41 | local status 42 | status, message = pcall(function() 43 | local s = sets[i] 44 | if s[1] then 45 | new_sets[i] = ref:read(s[1]) 46 | else 47 | new_sets[i] = ref:read(s) 48 | end 49 | end) 50 | if not status then 51 | error(("error: sets.list:read: didn't understand set argument %s.\n %s") 52 | :format(lib.repr(sets[i]), message)) 53 | end 54 | end 55 | return list:new(new_sets) 56 | end 57 | 58 | 59 | function list:__add(i) 60 | new_list = {} 61 | for j, v in ipairs(self) do new_list[j] = v end 62 | new_list[#new_list+1] = ref:read(i) 63 | return list:new(new_list) 64 | end 65 | 66 | 67 | function list:append(i) 68 | self[#self+1] = ref:read(i) 69 | end 70 | 71 | 72 | function list:pop() 73 | self[#self] = nil 74 | end 75 | 76 | 77 | function list:__repr(format) 78 | return "{"..lib.concat_repr(self, format, "}{").."}" 79 | end 80 | list.__tostring = lib.__tostring 81 | 82 | 83 | -- Prepare an expression for evaluation ---------------------------------------- 84 | 85 | function list:prepare(e, name) 86 | local S = scope.new() 87 | for i, s in ipairs(self) do 88 | self[i] = core.eval(s, S) 89 | for _, n in ipairs(s.names) do 90 | scope.newindex(S, n, index:new(nil, name, n)) 91 | end 92 | end 93 | if e then 94 | return core.eval(e, S) 95 | end 96 | end 97 | 98 | 99 | function list:unprepare(e, name) 100 | local S = scope.new() 101 | local Sl = scope.index(S, name) 102 | for i, s in ipairs(self) do 103 | for _, n in ipairs(s.names) do 104 | index.newindex(Sl, n, index:new(nil, n)) 105 | end 106 | end 107 | return core.eval(e, S) 108 | end 109 | 110 | 111 | -- Set a ref in a scope -------------------------------------------------------- 112 | 113 | function list:set_args(S, Sn, i, a) 114 | self[i]:index(S, Sn, a) 115 | end 116 | 117 | 118 | -- Iteration ------------------------------------------------------------------- 119 | 120 | function list:iterate(S, name) 121 | local undefined_sets = {} 122 | local Sn = scope.index(S, name) 123 | 124 | local function z(i) 125 | i = i or 1 126 | if not self[i] then 127 | local ud 128 | if undefined_sets[1] then ud = list.copy(undefined_sets) end 129 | coroutine.yield(S, ud) 130 | else 131 | local it = core.eval(self[i], S) 132 | if core.defined(it) then 133 | for _ in it:iterate(Sn) do 134 | z(i+1) 135 | end 136 | else 137 | undefined_sets[#undefined_sets+1] = it 138 | for _, n in ipairs(it.names) do 139 | index.newindex(Sn, n, nil) 140 | end 141 | z(i+1) 142 | undefined_sets[#undefined_sets] = nil 143 | end 144 | end 145 | end 146 | 147 | return coroutine.wrap(z) 148 | end 149 | 150 | 151 | -- EOF ------------------------------------------------------------------------- 152 | 153 | -------------------------------------------------------------------------------- /lua/rima/expression.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local object = require("rima.lib.object") 5 | local lib = require("rima.lib") 6 | local core = require("rima.core") 7 | 8 | 9 | ------------------------------------------------------------------------------ 10 | 11 | local expression = object:new_class({}, "expression") 12 | local expressions = setmetatable({}, { __mode = "k" }) 13 | 14 | 15 | local function wrap(op) 16 | if type(op) ~= "table" then return op end 17 | 18 | local top = object.typeinfo(op) 19 | if top.index or top.operator then 20 | local e = setmetatable({}, expression) 21 | expressions[e] = op 22 | return e 23 | else 24 | return op 25 | end 26 | end 27 | 28 | 29 | local function vtwrap(...) 30 | local r = {} 31 | for i = 1, select("#", ...) do 32 | r[i] = wrap(select(i, ...)) 33 | end 34 | return r 35 | end 36 | 37 | 38 | local function vwrap(...) 39 | return unpack(vtwrap(...)) 40 | end 41 | 42 | 43 | local function unwrap(e) 44 | return expressions[e] or e 45 | end 46 | 47 | 48 | local function tunwrap(t) 49 | local r = {} 50 | for k, v in pairs(t) do 51 | r[k] = unwrap(v) 52 | end 53 | return r 54 | end 55 | 56 | 57 | local function vtunwrap(...) 58 | local r = {} 59 | for i = 1, select("#", ...) do 60 | r[i] = unwrap(select(i, ...)) 61 | end 62 | return r 63 | end 64 | 65 | 66 | local function vunwrap(...) 67 | return unpack(vtunwrap(...)) 68 | end 69 | 70 | 71 | expression.wrap = wrap 72 | expression.vtwrap = vtwrap 73 | expression.vwrap = vwrap 74 | expression.unwrap = unwrap 75 | expression.tunwrap = tunwrap 76 | expression.vtunwrap = vtunwrap 77 | expression.vunwrap = vunwrap 78 | 79 | 80 | ------------------------------------------------------------------------------ 81 | 82 | local W, U = wrap, unwrap 83 | 84 | 85 | function expression:__list_variables(S, list) 86 | return core.list_variables(U(self), S, list) 87 | end 88 | 89 | 90 | function expression:__repr(format, ...) 91 | local s = lib.repr(unwrap(self), format, ...) 92 | if format.format == "dump" then 93 | return "expression("..s..")" 94 | else 95 | return s 96 | end 97 | end 98 | expression.__tostring = lib.__tostring 99 | 100 | 101 | ------------------------------------------------------------------------------ 102 | 103 | local ops = require"rima.operations" 104 | local call = require"rima.operators.call" 105 | local index = require"rima.index" 106 | 107 | 108 | function expression.__add (a, b) return W(ops.add(U(a), U(b))) end 109 | function expression.__sub (a, b) return W(ops.sub(U(a), U(b))) end 110 | function expression.__unm (a) return W(ops.unm(U(a))) end 111 | function expression.__mul (a, b) return W(ops.mul(U(a), U(b))) end 112 | function expression.__div (a, b) return W(ops.div(U(a), U(b))) end 113 | function expression.__pow (a, b) return W(ops.pow(U(a), U(b))) end 114 | function expression.__mod (a, b) return W(ops.mod(U(a), U(b))) end 115 | function expression.__call (...) return W(call:new(vtunwrap(...))) end 116 | 117 | 118 | function expression.__index(t, k, ...) 119 | if type(k) == "table" and not getmetatable(k) then 120 | local k2 = {} 121 | for k, v in pairs(k) do 122 | k2[U(k)] = U(v) 123 | end 124 | k = k2 125 | else 126 | k = U(k) 127 | end 128 | return W(index:new(U(t), k, vunwrap(...))) 129 | end 130 | 131 | 132 | function expression.__newindex(e, k, v) 133 | e = U(e) 134 | 135 | if type(k) == "table" and not getmetatable(k) then 136 | local k2 = {} 137 | for k, v in pairs(k) do 138 | k2[U(k)] = U(v) 139 | end 140 | k = k2 141 | else 142 | k = U(k) 143 | end 144 | 145 | if object.typename(e) == "index" then 146 | index.newindex(e, k, U(v), 1) 147 | else 148 | error("You can't do that!") 149 | end 150 | end 151 | 152 | 153 | ------------------------------------------------------------------------------ 154 | 155 | return expression 156 | 157 | ------------------------------------------------------------------------------ 158 | 159 | -------------------------------------------------------------------------------- /docs/knapsack.txt: -------------------------------------------------------------------------------- 1 | # Rima: The Knapsack Problem 2 | 3 | [ [Contents](contents.html) ] 4 | 5 | This is a Rima version of the "burglar bill" 6 | [XPress knapsack example](http://dashoptimization.com/home/cgi-bin/example.pl?id=mosel_model_2_2). 7 | A knapsack problem involves picking items to fill a knapsack of limited 8 | capacity with items of maximum value. 9 | We have a list of items, each with a weight and capacity, and wish to choose 10 | which we'll take. 11 | 12 | We start by requiring the Rima module: 13 | 14 | rima = require("rima") 15 | 16 | Then we define some variables, or *references* with `rima.R`. 17 | Note that at this stage we don't have to tell Rima anything about the 18 | types of our variables, or whether they're parameters or variables we 19 | wish to solve for. 20 | We define references to our list of items, a reference to use for a single item 21 | and the total capacity of the knapsack: 22 | 23 | --! continue 24 | i, items = rima.R"i, items" 25 | capacity = rima.R"capacity" 26 | 27 | Next, we define our two equations, one for the value of the items we're 28 | putting in the knapsack, and one for the weight of the items. 29 | Note that we're using syntax like `item.take`, `item.value` and `item.size` - 30 | Rima understands data structures like a dynamic language should. 31 | Also, we haven't defined these fields or structures at all, 32 | when Rima has the data, it'll check it works as expected - duck typing for a 33 | modelling language! 34 | 35 | --! continue 36 | value = rima.sum{i=items}(i.take * i.value) 37 | size = rima.sum{i=items}(i.take * i.size) 38 | 39 | We create a new formulation, set its objective: 40 | 41 | --! continue 42 | knapsack = rima.mp.new() 43 | knapsack.sense = "maximise" 44 | knapsack.objective = value 45 | 46 | Rima accepts both `"maximise"` and `"maximize"` (as well as `"MaXiMiZe"`). 47 | 48 | We use `rima.mp.C` to construct a constraint, 49 | and add it to the scope like any other value:: 50 | 51 | --! continue 52 | knapsack.capacity_limit = rima.mp.C(size, "<=", capacity) 53 | 54 | Finally, we define `item.take` as a binary variable: 55 | 56 | --! continue 57 | knapsack.items[{i=items}].take = rima.binary() 58 | 59 | And we're done. `knapsack` is a complete definition of a knapsack problem. 60 | You can derive from it, compose a new model with it, make a Lua module out 61 | of it and we can even write it out in a readable format: 62 | 63 | --! continue 64 | print(knapsack) 65 | --> Maximise: 66 | --> sum{i in items}(i.take*i.value) 67 | --> Subject to: 68 | --> capacity_limit: sum{i in items}(i.size*i.take) <= capacity 69 | --> items[i].take binary for all i in items 70 | 71 | So far, we've seen no data, other than the one bit we wanted - 72 | defining as `items[item].picked` as a binary variable. 73 | Of course, we can't solve anything without data. 74 | 75 | Data is easy to add: it's just a regular Lua table: 76 | 77 | --! continue 78 | burglar_bill = \ 79 | { \ 80 | capacity = 102, \ 81 | items = \ 82 | { \ 83 | camera = { value = 15, size = 2 }, \ 84 | necklace = { value = 100, size = 20 }, \ 85 | vase = { value = 15, size = 20 }, \ 86 | picture = { value = 15, size = 30 }, \ 87 | tv = { value = 15, size = 40 }, \ 88 | video = { value = 15, size = 30 }, \ 89 | chest = { value = 15, size = 60 }, \ 90 | brick = { value = 1, size = 10 }, \ 91 | } \ 92 | } 93 | 94 | To solve the problem use the `solve` method to combine the model and data: 95 | 96 | --! continue 97 | primal, dual = rima.mp.solve(knapsack, burglar_bill) 98 | 99 | We can check the objective: 100 | 101 | --! continue 102 | print(primal.objective) --> 160 103 | 104 | And see if it was worth taking the brick or necklace in the knapsack: 105 | 106 | --! continue 107 | print(primal.items.brick.take) --> 0 108 | print(primal.items.necklace.take) --> 1 109 | 110 | [ [Contents](contents.html) ] 111 | -------------------------------------------------------------------------------- /lua/rima/solvers/ipopt.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local math = require("math") 5 | local assert, getmetatable, ipairs, pcall = assert, getmetatable, ipairs, pcall 6 | 7 | local interface = require("rima.interface") 8 | local ops = require("rima.operations") 9 | 10 | local status, ipopt_core = pcall(require, "rima_ipopt_core") 11 | 12 | module(...) 13 | 14 | 15 | -------------------------------------------------------------------------------- 16 | 17 | available = status 18 | problem = not available and ipopt_core 19 | objective = { linear = true, nonlinear = true } 20 | constraints = { linear = true, nonlinear = true } 21 | variables = { continuous = true } 22 | 23 | preference = 3 24 | 25 | 26 | -------------------------------------------------------------------------------- 27 | 28 | local function compile_jacobian(expressions, variables) 29 | local sparsity = {} 30 | local e2 = {} 31 | 32 | local function add_expression(e, i) 33 | for j, v in ipairs(variables) do 34 | local dedv = interface.diff(e, v.ref) 35 | if dedv ~= 0 then 36 | sparsity[#sparsity+1] = {i, j} 37 | e2[#e2+1] = dedv 38 | end 39 | end 40 | end 41 | 42 | if getmetatable(expressions) then 43 | add_expression(expressions, 1) 44 | else 45 | for i, e in ipairs(expressions) do 46 | add_expression(e, i) 47 | end 48 | end 49 | 50 | local f, function_string = interface.compile(e2, variables) 51 | return f, function_string, sparsity 52 | end 53 | 54 | 55 | local function compile_hessian(objective, constraints, variables) 56 | local sigma, lambda = interface.R"sigma, lambda" 57 | 58 | local sparsity = {} 59 | local e2 = {} 60 | 61 | for i, v1 in ipairs(variables) do 62 | for j, v2 in ipairs(variables) do 63 | local exp, nonzero = 0, false 64 | local do2dv1dv2 = interface.diff(interface.diff(objective, v1.ref), v2.ref) 65 | if do2dv1dv2 ~= 0 then 66 | exp = sigma * do2dv1dv2 67 | nonzero = true 68 | end 69 | 70 | for k, c in ipairs(constraints) do 71 | local dc2dv1dv2 = interface.diff(interface.diff(c, v1.ref), v2.ref) 72 | if dc2dv1dv2 ~= 0 then 73 | exp = exp + lambda[k] * dc2dv1dv2 74 | nonzero = true 75 | end 76 | end 77 | if nonzero then 78 | sparsity[#sparsity+1] = {i, j} 79 | e2[#e2+1] = exp 80 | end 81 | end 82 | end 83 | 84 | local f, function_string = interface.compile(e2, variables, "args, sigma, lambda") 85 | return f, function_string, sparsity 86 | end 87 | 88 | 89 | -------------------------------------------------------------------------------- 90 | 91 | local function solve_(options) 92 | for _, v in ipairs(options.ordered_variables) do 93 | if v.type.lower == -math.huge then 94 | if v.type.upper == math.huge then 95 | v.initial = 0 96 | else 97 | v.initial = v.type.upper 98 | end 99 | elseif v.type.upper == math.huge then 100 | v.initial = v.type.lower 101 | else 102 | v.initial = (v.type.lower+v.type.upper)/2 103 | end 104 | end 105 | 106 | if options.sense == "maximise" then options.objective = ops.unm(options.objective) end 107 | 108 | local F = 109 | { 110 | variables = options.ordered_variables, 111 | constraint_bounds = options.constraint_info, 112 | objective_function = interface.compile(options.objective, options.ordered_variables), 113 | constraint_function = interface.compile(options.constraint_expressions, options.ordered_variables) 114 | } 115 | F.objective_jacobian, _, F.oj_sparsity = compile_jacobian(options.objective, options.ordered_variables) 116 | F.constraint_jacobian, _, F.cj_sparsity = compile_jacobian(options.constraint_expressions, options.ordered_variables) 117 | F.hessian, _, F.hessian_sparsity = compile_hessian(options.objective, options.constraint_expressions, options.ordered_variables) 118 | 119 | local M = assert(ipopt_core.new(F)) 120 | return M:solve() 121 | end 122 | 123 | solve = available and solve_ or nil 124 | 125 | 126 | -- EOF ------------------------------------------------------------------------- 127 | -------------------------------------------------------------------------------- /lua/rima/lib.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local object = require("rima.lib.object") 5 | local typename = object.typename 6 | 7 | 8 | ------------------------------------------------------------------------------ 9 | 10 | local lib = {} 11 | 12 | ------------------------------------------------------------------------------ 13 | 14 | function lib.getmetamethod(obj, methodname) 15 | local mt = getmetatable(obj) 16 | return mt and mt[methodname] 17 | end 18 | 19 | 20 | function lib.append(t, ...) 21 | local l = #t 22 | for i = 1, select("#", ...) do 23 | t[l + i] = select(i, ...) 24 | end 25 | end 26 | 27 | 28 | function lib.imap(f, t) 29 | local r = {} 30 | for i, v in ipairs(t) do r[i] = f(v) end 31 | return r 32 | end 33 | 34 | 35 | function lib.concat(t, s, f) 36 | t = (f and lib.imap(f, t)) or t 37 | return table.concat(t, s) 38 | end 39 | 40 | 41 | ------------------------------------------------------------------------------ 42 | 43 | -- Convert an object if it has a metamethod with the right name 44 | function lib.convert(obj, methodname) 45 | local f = lib.getmetamethod(obj, methodname) 46 | if f then 47 | return f(obj) 48 | else 49 | return obj 50 | end 51 | end 52 | 53 | 54 | ------------------------------------------------------------------------------ 55 | 56 | local function is_identifier(v) 57 | return v:match("^[_%a][_%w]*$") 58 | end 59 | 60 | 61 | function lib.is_identifier_string(v) 62 | return type(v) == "string" and is_identifier(v) 63 | end 64 | 65 | 66 | ------------------------------------------------------------------------------ 67 | 68 | local number_format = "%.4g" 69 | function lib.set_number_format(f) 70 | number_format = f 71 | end 72 | 73 | 74 | function lib.simple_repr(o, format) 75 | local t = typename(o) 76 | if t == "number" then 77 | local nf = format.numbers or number_format 78 | return nf:format(o) 79 | elseif format.format == "dump" then 80 | if t == "string" then 81 | return ("%q"):format(o) 82 | elseif t == "boolean" then 83 | return tostring(o) 84 | elseif t == "table" then 85 | local s = "table"..": { " 86 | local count = 0 87 | for k, v in pairs(o) do 88 | if count == 3 then s = s..",..." break end 89 | if count > 0 then s = s..", " end 90 | s = s..tostring(k).."="..tostring(v) 91 | count = count + 1 92 | end 93 | s = s.." }" 94 | return s 95 | elseif t == "nil" then 96 | return "nil" 97 | else 98 | return t.."("..tostring(o)..")" 99 | end 100 | elseif format.format == "latex" then 101 | if t == "table" then 102 | return "\\text{table}" 103 | elseif t == "boolean" then 104 | return "\\text{"..tostring(o).."}" 105 | elseif t == "nil" then 106 | return "\\text{nil}" 107 | elseif t == "string" then 108 | if o:len() == 1 then 109 | return o 110 | elseif is_identifier(o) then 111 | return "\\text{"..o:gsub("_", "\\_").."}" 112 | else 113 | return "\\text{``"..o:gsub("_", "\\_").."''}" 114 | end 115 | else 116 | return tostring(o) 117 | end 118 | else 119 | if t == "table" then 120 | return "table" 121 | else 122 | return tostring(o) 123 | end 124 | end 125 | end 126 | 127 | 128 | local no_format = {} 129 | 130 | function lib.repr(o, format) 131 | format = format or no_format 132 | 133 | local f = lib.getmetamethod(o, "__repr") 134 | if f then 135 | return f(o, format) 136 | else 137 | return lib.simple_repr(o, format) 138 | end 139 | end 140 | 141 | 142 | function lib.__tostring(o) 143 | return getmetatable(o).__repr(o, no_format) 144 | end 145 | 146 | 147 | function lib.concat_repr(t, format, sep) 148 | return lib.concat(t, sep or ", ", function(i) return lib.repr(i, format) end) 149 | end 150 | 151 | 152 | local dump_format = { format="dump" } 153 | function lib.dump(e) 154 | return lib.repr(e, dump_format) 155 | end 156 | 157 | 158 | ------------------------------------------------------------------------------ 159 | 160 | return lib 161 | 162 | ------------------------------------------------------------------------------ 163 | 164 | -------------------------------------------------------------------------------- /lua/rima/core.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2011 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local lib = require("rima.lib") 5 | local trace = require("rima.lib.trace") 6 | local object = require("rima.lib.object") 7 | 8 | local typeinfo = object.typeinfo 9 | 10 | 11 | ------------------------------------------------------------------------------ 12 | 13 | local core = {} 14 | 15 | ------------------------------------------------------------------------------ 16 | 17 | --- Is an expression defined? 18 | function core.defined(e) 19 | local f = lib.getmetamethod(e, "__defined") 20 | if trace.on then trace.enter("defd", 1, f, e) end 21 | local d 22 | 23 | if f then 24 | d = f(e) 25 | else 26 | d = not lib.getmetamethod(e, "__eval") 27 | end 28 | if trace.on then trace.leave("defd", 1, e, d) end 29 | return d 30 | end 31 | 32 | 33 | ------------------------------------------------------------------------------ 34 | 35 | --- Is an expression arithmetic? 36 | -- An expression is arithmetic if we can add to it. 37 | function core.arithmetic(e) 38 | if trace.on then trace.enter("arth", 1, nil, e) end 39 | local result 40 | 41 | if type(e) == "number" then 42 | result = true 43 | else 44 | local mt = getmetatable(e) 45 | if not mt then 46 | result = false 47 | else 48 | local f 49 | f = mt.__arithmetic 50 | if f then 51 | result = f(e) 52 | else 53 | f = mt.__defined 54 | if f then 55 | result = f(e) 56 | elseif mt.__eval then 57 | result = false 58 | else 59 | result = mt.__add and true 60 | end 61 | end 62 | end 63 | end 64 | if trace.on then trace.leave("arth", 1, e, result) end 65 | return result 66 | end 67 | 68 | 69 | ------------------------------------------------------------------------------ 70 | 71 | function core.eval(e, S) 72 | if trace.on then trace.enter("eval", 1, nil, e) end 73 | local value, type, addr = core.eval_to_paths(e, S, 1) 74 | local f = lib.getmetamethod(value, "__finish") 75 | if f then 76 | value, type, addr = f(value) 77 | end 78 | if typeinfo(value).undefined_t then 79 | type = value 80 | value = nil 81 | end 82 | value = value or addr 83 | if trace.on then trace.leave("eval", 1, e, value, type, addr) end 84 | return value, type, addr 85 | end 86 | 87 | 88 | function core.eval_to_paths(e, s, d) 89 | local f = lib.getmetamethod(e, "__eval") 90 | if trace.on then trace.enter("evtp", d and d+1, f, e) end 91 | local value, type, addr 92 | if f then 93 | value, type, addr = f(e, s) 94 | else 95 | value = e 96 | end 97 | value = value or addr 98 | if trace.on then trace.leave("evtp", 1, e, value, type, addr) end 99 | return value, type, addr 100 | end 101 | 102 | 103 | ------------------------------------------------------------------------------ 104 | 105 | --- Differentiate e with respect to v 106 | function core.diff(e, v) 107 | local f = lib.getmetamethod(e, "__diff") 108 | if trace.on then trace.enter("diff", d and d+1, f, e, v) end 109 | local dedv 110 | if f then 111 | dedv = f(e, v) 112 | elseif type(e) == "number" then 113 | dedv = 0 114 | else 115 | error(("Can't differentiate %s with respect to %s"):format(lib.repr(e), lib.repr(v))) 116 | end 117 | if trace.on then trace.leave("diff", 1, e, v, dedv) end 118 | return dedv 119 | end 120 | 121 | 122 | ------------------------------------------------------------------------------ 123 | 124 | function core.list_variables(e, S, list) 125 | list = list or {} 126 | local f = lib.getmetamethod(e, "__list_variables") 127 | if f then f(e, S, list) end 128 | return list 129 | end 130 | 131 | 132 | ------------------------------------------------------------------------------ 133 | 134 | function core.parenthise(e, format, parent_precedence) 135 | parent_precedence = parent_precedence or 1 136 | local s = lib.repr(e, format) 137 | local precedence = lib.getmetamethod(e, "precedence") or 0 138 | if precedence > parent_precedence then 139 | s = "("..s..")" 140 | end 141 | return s 142 | end 143 | 144 | 145 | ------------------------------------------------------------------------------ 146 | 147 | return core 148 | 149 | ------------------------------------------------------------------------------ 150 | 151 | -------------------------------------------------------------------------------- /htmldocs/styles/shThemeDefault.css: -------------------------------------------------------------------------------- 1 | /** 2 | * SyntaxHighlighter 3 | * http://alexgorbatchev.com/ 4 | * 5 | * SyntaxHighlighter is donationware. If you are using it, please donate. 6 | * http://alexgorbatchev.com/wiki/SyntaxHighlighter:Donate 7 | * 8 | * @version 9 | * 2.1.364 (October 15 2009) 10 | * 11 | * @copyright 12 | * Copyright (C) 2004-2009 Alex Gorbatchev. 13 | * 14 | * @license 15 | * This file is part of SyntaxHighlighter. 16 | * 17 | * SyntaxHighlighter is free software: you can redistribute it and/or modify 18 | * it under the terms of the GNU Lesser General Public License as published by 19 | * the Free Software Foundation, either version 3 of the License, or 20 | * (at your option) any later version. 21 | * 22 | * SyntaxHighlighter is distributed in the hope that it will be useful, 23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 | * GNU General Public License for more details. 26 | * 27 | * You should have received a copy of the GNU General Public License 28 | * along with SyntaxHighlighter. If not, see . 29 | */ 30 | /************************************ 31 | * Default Syntax Highlighter theme. 32 | * 33 | * Interface elements. 34 | ************************************/ 35 | 36 | .syntaxhighlighter 37 | { 38 | background-color: #fff !important; 39 | } 40 | 41 | /* Highlighed line number */ 42 | .syntaxhighlighter .line.highlighted .number 43 | { 44 | color: black !important; 45 | } 46 | 47 | /* Highlighed line */ 48 | .syntaxhighlighter .line.highlighted.alt1, 49 | .syntaxhighlighter .line.highlighted.alt2 50 | { 51 | background-color: #e0e0e0 !important; 52 | } 53 | 54 | /* Gutter line numbers */ 55 | .syntaxhighlighter .line .number 56 | { 57 | color: #afafaf !important; 58 | } 59 | 60 | /* Add border to the lines */ 61 | .syntaxhighlighter .line .content 62 | { 63 | border-left: 3px solid #6CE26C !important; 64 | color: #000 !important; 65 | } 66 | 67 | .syntaxhighlighter.printing .line .content 68 | { 69 | border: 0 !important; 70 | } 71 | 72 | /* First line */ 73 | .syntaxhighlighter .line.alt1 74 | { 75 | background-color: #fff !important; 76 | } 77 | 78 | /* Second line */ 79 | .syntaxhighlighter .line.alt2 80 | { 81 | background-color: #F8F8F8 !important; 82 | } 83 | 84 | .syntaxhighlighter .toolbar 85 | { 86 | background-color: #F8F8F8 !important; 87 | border: #E7E5DC solid 1px !important; 88 | } 89 | 90 | .syntaxhighlighter .toolbar a 91 | { 92 | color: #a0a0a0 !important; 93 | } 94 | 95 | .syntaxhighlighter .toolbar a:hover 96 | { 97 | color: red !important; 98 | } 99 | 100 | /************************************ 101 | * Actual syntax highlighter colors. 102 | ************************************/ 103 | .syntaxhighlighter .plain, 104 | .syntaxhighlighter .plain a 105 | { 106 | color: #000 !important; 107 | } 108 | 109 | .syntaxhighlighter .comments, 110 | .syntaxhighlighter .comments a 111 | { 112 | color: #008200 !important; 113 | } 114 | 115 | .syntaxhighlighter .string, 116 | .syntaxhighlighter .string a 117 | { 118 | color: blue !important; 119 | } 120 | 121 | .syntaxhighlighter .keyword 122 | { 123 | color: #069 !important; 124 | font-weight: bold !important; 125 | } 126 | 127 | .syntaxhighlighter .preprocessor 128 | { 129 | color: gray !important; 130 | } 131 | 132 | .syntaxhighlighter .variable 133 | { 134 | color: #a70 !important; 135 | } 136 | 137 | .syntaxhighlighter .value 138 | { 139 | color: #090 !important; 140 | } 141 | 142 | .syntaxhighlighter .functions 143 | { 144 | color: #ff1493 !important; 145 | } 146 | 147 | .syntaxhighlighter .constants 148 | { 149 | color: #0066CC !important; 150 | } 151 | 152 | .syntaxhighlighter .script 153 | { 154 | background-color: yellow !important; 155 | } 156 | 157 | .syntaxhighlighter .color1, 158 | .syntaxhighlighter .color1 a 159 | { 160 | /* color: #808080 !important;*/ 161 | font-weight: bold !important; 162 | } 163 | 164 | .syntaxhighlighter .color2, 165 | .syntaxhighlighter .color2 a 166 | { 167 | color: #ff1493 !important; 168 | } 169 | 170 | .syntaxhighlighter .color3, 171 | .syntaxhighlighter .color3 a 172 | { 173 | color: red !important; 174 | } 175 | -------------------------------------------------------------------------------- /lua/tests/rima/mp.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2012 Incremental IP Limited 2 | -- see LICENSE for license information 3 | 4 | local mp = require("rima.mp") 5 | 6 | local lib = require("rima.lib") 7 | local number_t = require("rima.types.number_t") 8 | local interface = require("rima.interface") 9 | 10 | 11 | ------------------------------------------------------------------------------ 12 | 13 | return function(T) 14 | local R = interface.R 15 | local sum = interface.sum 16 | 17 | do 18 | local x, y = R"x, y" 19 | local S = mp.new() 20 | S.c1 = interface.mp.constraint(x + 2*y, "<=", 3) 21 | S.c2 = interface.mp.constraint(2*x + y, "<=", 3) 22 | S.objective = x + y 23 | S.sense = "maximise" 24 | S.x = number_t.positive() 25 | S.y = number_t.positive() 26 | T:check_equal(lib.repr(S), 27 | [[ 28 | Maximise: 29 | x + y 30 | Subject to: 31 | c1: x + 2*y <= 3 32 | c2: 2*x + y <= 3 33 | 0 <= x <= inf, x real 34 | 0 <= y <= inf, y real 35 | ]]) 36 | 37 | local primal, dual = mp.solve(S) 38 | if primal then 39 | T:check_equal(primal.objective, 2) 40 | T:check_equal(primal.x, 1) 41 | T:check_equal(primal.y, 1) 42 | T:check_equal(primal.c1, 3) 43 | T:check_equal(primal.c2, 3) 44 | T:check_equal(math.abs(dual.x), 0) 45 | T:check_equal(math.abs(dual.y), 0) 46 | T:check_equal(dual.c1, 1/3) 47 | T:check_equal(dual.c2, 1/3) 48 | end 49 | end 50 | 51 | do 52 | local x, i, j = R"x, i, j" 53 | local S = mp.new() 54 | S.c1 = interface.mp.constraint(x[1][1].a + 2*x[1][2].a, "<=", 3) 55 | S.c2 = interface.mp.constraint(2*x[1][1].a + x[1][2].a, "<=", 3) 56 | S.objective = x[1][1].a + x[1][2].a 57 | S.sense = "maximise" 58 | S.x[i][j].a = number_t.positive() 59 | T:check_equal(S, 60 | [[ 61 | Maximise: 62 | x[1, 1].a + x[1, 2].a 63 | Subject to: 64 | c1: x[1, 1].a + 2*x[1, 2].a <= 3 65 | c2: 2*x[1, 1].a + x[1, 2].a <= 3 66 | 0 <= x[i, j].a <= inf, x[i, j].a real for all i, j 67 | ]]) 68 | 69 | local primal, dual = mp.solve_with("cbc", S) 70 | 71 | if primal then 72 | T:check_equal(primal.objective, 2) 73 | T:check_equal(primal.x[1][1].a, 1) 74 | T:check_equal(primal.x[1][2].a, 1) 75 | T:check_equal(primal.c1, 3) 76 | T:check_equal(primal.c2, 3) 77 | end 78 | end 79 | 80 | do 81 | local m, M, n, N = R"m, M, n, N" 82 | local A, b, c, x = R"A, b, c, x" 83 | local S = mp.new() 84 | S.constraint[{m=M}] = interface.mp.constraint(sum{n=N}(A[m][n] * x[n]), "<=", b[m]) 85 | S.objective = sum{n=N}(c[n] * x[n]) 86 | S.sense = "maximise" 87 | S.x[n] = number_t.positive() 88 | T:check_equal(S, 89 | [[ 90 | Maximise: 91 | sum{n in N}(c[n]*x[n]) 92 | Subject to: 93 | constraint[m in M]: sum{n in N}(A[m, n]*x[n]) <= b[m] 94 | 0 <= x[n] <= inf, x[n] real for all n 95 | ]]) 96 | 97 | local primal, dual = mp.solve_with("lpsolve", S, 98 | { 99 | M = interface.range(1, 2), 100 | N = interface.range(1, 2), 101 | A = {{1, 2}, {2, 1}}, 102 | b = {3, 3}, 103 | c = {1, 1}, 104 | }) 105 | 106 | if primal then 107 | T:check_equal(primal.objective, 2) 108 | T:check_equal(primal.x[1], 1) 109 | T:check_equal(primal.x[2], 1) 110 | T:check_equal(primal.constraint[1], 3) 111 | T:check_equal(primal.constraint[2], 3) 112 | end 113 | end 114 | 115 | do 116 | local a, p, P, q, Q = R"a, p, P, q, Q" 117 | local S = mp.new() 118 | S.constraint1[{p=P}][{q=P[p].Q}] = interface.mp.constraint(a, "<=", P[p].Q[q]) 119 | S.constraint2[{p=P}][{q=p.Q}] = interface.mp.constraint(a, "<=", q) 120 | T:check_equal(S, 121 | [[ 122 | No objective defined 123 | Subject to: 124 | constraint1[p in P, q in P[p].Q]: a <= P[p].Q[q] 125 | constraint2[p in P, q in p.Q]: a <= q 126 | ]]) 127 | S.P = {{Q={3,5}},{Q={7,11,13}}} 128 | T:check_equal(S, 129 | [[ 130 | No objective defined 131 | Subject to: 132 | constraint1[1, 3]: a <= 3 133 | constraint1[1, 5]: a <= 5 134 | constraint1[2, 7]: a <= 7 135 | constraint1[2, 11]: a <= 11 136 | constraint1[2, 13]: a <= 13 137 | constraint2[1, 3]: a <= 3 138 | constraint2[1, 5]: a <= 5 139 | constraint2[2, 7]: a <= 7 140 | constraint2[2, 11]: a <= 11 141 | constraint2[2, 13]: a <= 13 142 | ]]) 143 | end 144 | end 145 | 146 | 147 | ------------------------------------------------------------------------------ 148 | --------------------------------------------------------------------------------