Skip to content

Commit e4ff819

Browse files
author
Wouter Coppieters
committed
Added code fences for syntax highlighting
1 parent 4438c4b commit e4ff819

File tree

7 files changed

+93
-52
lines changed

7 files changed

+93
-52
lines changed

README.md

Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Rulp is inspired by the ruby wrapper for the GLPK toolkit and the python LP libr
2727

2828
## Sample Code
2929

30+
```ruby
3031
# maximize
3132
# z = 10 * x + 6 * y + 4 * z
3233
#
@@ -62,12 +63,13 @@ Rulp is inspired by the ruby wrapper for the GLPK toolkit and the python LP libr
6263
# Y_i.value == 67
6364
# Z_i.value == 0
6465
##
65-
66+
```
6667

6768
## Usage
6869

6970
#### Variables
7071

72+
```ruby
7173
# Rulp variables are initialized as soon as they are needed so there is no
7274
# need to initialize them.
7375
# They follow a naming convention that defines their type.
@@ -110,7 +112,7 @@ Rulp is inspired by the ruby wrapper for the GLPK toolkit and the python LP libr
110112
#<LV:0x007ffc4cc0d460 @name="Unit8">,
111113
#<LV:0x007ffc4cc0cee8 @name="Unit9">,
112114
#<LV:0x007ffc4cc0c970 @name="Unit10">]
113-
115+
```
114116

115117
### Variable Constraints
116118

@@ -119,6 +121,7 @@ Be careful to use '==' and not '=' when expressing equality.
119121
Constraints on a variable can only use numeric literals and not other variables.
120122
Inter-variable constraints should be expressed as problem constrants. (Explained below.)
121123

124+
```ruby
122125
X_i < 5
123126
X_i.bounds
124127
=> "X <= 5"
@@ -130,11 +133,13 @@ Inter-variable constraints should be expressed as problem constrants. (Explained
130133
Y_f == 10
131134
Y_f.bounds
132135
=> "y = 10"
136+
```
133137

134138
### Problem constraints
135139

136140
Constraints are added to a problem using the :[] syntax.
137141

142+
```ruby
138143
problem = Rulp::Cbc Rulp::Max( 10 * X_i + 6 * Y_i + 4 * Z_i )
139144

140145
problem[
@@ -146,14 +151,17 @@ Constraints are added to a problem using the :[] syntax.
146151
]
147152
...
148153
problem.solve
154+
```
149155

150156
You can add multiple constraints at once by comma separating them as seen in the earlier examples:
151157

158+
```ruby
152159
Rulp::Cbc Rulp::Max( 10 * X_i + 6 * Y_i + 4 * Z_i ) [
153160
X_i + Y_i + Z_i <= 100,
154161
10 * X_i + 4 * Y_i + 5 * Z_i <= 600,
155162
2 * X_i + 2 * Y_i + 6 * Z_i <= 300
156163
]
164+
```
157165

158166
### Solving or saving 'lp' files
159167

@@ -165,61 +173,76 @@ such that the command `which [exec_name]` returns a path. (I.e they must be on y
165173

166174
Given a problem there are multiple ways to initiate a solver.
167175

176+
```ruby
168177
@problem = Rulp::Cbc Rulp::Max( 10 * X_i + 6 * Y_i + 4 * Z_i ) [
169178
X_i + Y_i + Z_i <= 100,
170179
10 * X_i + 4 * Y_i + 5 * Z_i <= 600,
171180
2 * X_i + 2 * Y_i + 6 * Z_i <= 300
172181
]
182+
```
173183

174184
Default solver:
175185

186+
```ruby
176187
@problem.solve
177188
# this will use the solver specified in the environment variable 'SOLVER' by default.
178189
# This can be 'scip', 'cbc', or 'glpk'. If no variable is given it uses scip as a default.
190+
```
179191

180192
If you had a linear equation in a file named 'problem.rb' from the command line you could specify an alternate solver by executing:
181193

194+
```ruby
182195
SOLVER=cbc ruby problem.rb
196+
```
183197

184198
Explicit solver:
185199

200+
```ruby
186201
@problem.scip
187202
# Or
188203
@problem.cbc
189204
# Or
190205
@problem.glpk
206+
```
191207

192208
Or
193209

210+
```ruby
194211
Rulp::Scip(@problem)
195212
Rulp::Glpk(@problem)
196213
Rulp::Cbc(@problem)
197-
214+
```
198215

199216
For debugging purposes you may wish to see the input and output files generated and consumed by Rulp.
200217
To do this you can use the following extended syntax:
201218

219+
```ruby
202220
def solve_with(type, open_definition=false, open_solution=false)...
221+
```
203222
204223
The optional booleans will optionally call the 'open' utility to open the problem definition or the solution. (This utility is installed by default on a mac and will not work if the utility is not on your PATH)
205224
206-
225+
```ruby
207226
@problem.solve_with(SCIP, true, true)
227+
```
208228

209229
#### Saving LP files.
210230

211231
You may not wish to use one of the RULP compatible but another solver that is able to read .lp files. (E.g CPLEX or Gurobi) but still want to use Rulp to generate your LP file. In this case you should use Rulp to output your lp problem description to a file of your choice. To do this simply use the following call
212232

233+
```ruby
213234
@problem.save("/Users/johndoe/Desktop/myproblem.lp")
235+
```
214236

215237
OR
216-
238+
```ruby
217239
@problem.output("/Users/johndoe/Desktop/myproblem.lp")
240+
```
218241

219242
You should also be able to call
220-
243+
```ruby
221244
@problem.save
222-
245+
```
223246
Without parameters to be prompted for a save location.
224247

225248
### Examples.
@@ -230,6 +253,8 @@ Rulp comes bundled with a 'rulp' executable which by default loads the rulp envi
230253
(Preference for PRY). Once installed you should be able to simply execute 'rulp' to launch this rulp enabled REPL.
231254
You can then play with and attempt LP and MIP problems straight from the command line.
232255

256+
```ruby
257+
233258
[1] pry(main)> 13 <= X_i <= 45 # Declare integer variable
234259
=> X(i)[undefined]
235260

@@ -286,7 +311,7 @@ You can then play with and attempt LP and MIP problems straight from the command
286311

287312
[10] pry(main)> (2 * X_i + 15 * Y_f).evaluate
288313
=> 251.0
289-
314+
```
290315

291316
### A larger example
292317
Here is a basic example of how Rulp can help you model problems with a large number of variables.
@@ -298,18 +323,28 @@ of how we could use Rulp to formulate this problem.
298323
We decide to model each of these possible purchases as a binary variable (as we either purchase them or
299324
we don't. We can't partially purchase one.)
300325

301-
# Generate the data randomly for this example.
302-
costs, points = [*0..1000].map do |i|
303-
[Purchase_b(i) * Random.rand(1.0..3.0), Purchase_b(i) * Random.rand(5.0..10.0)]
304-
end.transpose.map(&:sum) #We sum the array of points and array of costs to create a Rulp expression
305-
306-
# And this is where the magic happens!. We ask rulp to maximise the number of points given
307-
# the constraint that costs must be less than $55
308-
309-
Rulp::Max(points)[
310-
costs < 55
311-
].solve
312-
=> 538.2125623353652 (# You will get a different value as data was generated randomly)
313-
314-
# Now how do we check which purchases were selected?
315-
selected_purchases = [*0..1000].select{|i| Purchase_b(i).value }
326+
```ruby
327+
# Generate the data randomly for this example.
328+
costs, points = [*0..1000].map do |i|
329+
[Purchase_b(i) * Random.rand(1.0..3.0), Purchase_b(i) * Random.rand(5.0..10.0)]
330+
end.transpose.map(&:sum) #We sum the array of points and array of costs to create a Rulp expression
331+
332+
# And this is where the magic happens!. We ask rulp to maximise the number of points given
333+
# the constraint that costs must be less than $55
334+
335+
Rulp::Max(points)[
336+
costs < 55
337+
].solve
338+
=> 538.2125623353652 (# You will get a different value as data was generated randomly)
339+
340+
# Now how do we check which purchases were selected?
341+
selected_purchases = [*0..1000].select do |i|
342+
Purchase_b(i).value
343+
end.map(&Purchase_b)
344+
=> [Purchase27(b)[true],
345+
Purchase86(b)[true],
346+
Purchase120(b)[true],
347+
Purchase141(b)[true],
348+
Purchase154(b)[true],
349+
...
350+
```

lib/extensions/kernel_extensions.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ def method_missing(value, *args)
2222
if (start <= "Z" && start >= "A")
2323
case method_name[-1]
2424
when "b"
25-
method_name = method_name[0..(method_name[-2] == "_" ? -3 : -2)] + args.join("_")
26-
return BV.send(method_name)
25+
method_name = method_name[0..(method_name[-2] == "_" ? -3 : -2)]
26+
return BV.send(method_name, args)
2727
when "i"
28-
method_name = method_name[0..(method_name[-2] == "_" ? -3 : -2)] + args.join("_")
29-
return IV.send(method_name)
28+
method_name = method_name[0..(method_name[-2] == "_" ? -3 : -2)]
29+
return IV.send(method_name, args)
3030
when "f"
31-
method_name = method_name[0..(method_name[-2] == "_" ? -3 : -2)] + args.join("_")
32-
return LV.send(method_name)
31+
method_name = method_name[0..(method_name[-2] == "_" ? -3 : -2)]
32+
return LV.send(method_name, args)
3333
end
3434
end
3535
old_method_missing(value, *args)

lib/rulp/lv.rb

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,49 @@
44
# These are constructed dynamically by using the special Capitalised variable declaration syntax.
55
##
66
class LV
7-
attr_reader :name
7+
attr_reader :name, :args
88
attr_accessor :lt, :lte, :gt, :gte, :value
99
include Rulp::Bounds
1010
include Rulp::Initializers
1111

1212
def to_proc
13-
->(index){ send(self.meth(index)) }
13+
->(index){ send(self.meth, index) }
1414
end
1515

16-
def meth(*args)
17-
"#{self.name}#{args.join("_")}_#{self.suffix}"
16+
def meth
17+
"#{self.name}_#{self.suffix}"
1818
end
1919

2020
def suffix
2121
"f"
2222
end
2323

2424
def self.method_missing(name, *args)
25-
return self.definition( "#{name}#{args.join("_")}" )
25+
return self.definition(name, args)
2626
end
2727

2828
def self.const_missing(name)
2929
return self.definition(name)
3030
end
3131

32-
def self.definition(name)
33-
self.class.send(:define_method, name){
34-
defined = LV::names_table["#{name}"]
35-
if defined && defined.class != self
36-
raise StandardError.new("ERROR:\n#{name} was already defined as a variable of type #{defined.class}."+
37-
"You are trying to redefine it as a variable of type #{self}")
38-
elsif(!defined)
39-
self.new(name)
32+
def self.definition(name, args)
33+
identifier = "#{name}#{args.join("_")}"
34+
self.class.send(:define_method, identifier){|index=nil|
35+
if index
36+
self.definition(name, index)
4037
else
41-
defined
38+
defined = LV::names_table["#{identifier}"]
39+
if defined && defined.class != self
40+
raise StandardError.new("ERROR:\n#{name} was already defined as a variable of type #{defined.class}."+
41+
"You are trying to redefine it as a variable of type #{self}")
42+
elsif(!defined)
43+
self.new(name, args)
44+
else
45+
defined
46+
end
4247
end
4348
}
44-
return self.send(name) || self.new(name)
49+
return self.send(identifier) || self.new(name, args)
4550
end
4651

4752
def * (numeric)
@@ -73,7 +78,7 @@ def value
7378
end
7479

7580
def inspect
76-
"#{name}(#{suffix})[#{value || 'undefined'}]"
81+
"#{name}#{args.join("-")}(#{suffix})[#{value || 'undefined'}]"
7782
end
7883
end
7984

lib/rulp/rulp_initializers.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
module Rulp
22
module Initializers
3-
def initialize(name)
4-
raise StandardError.new("Variable with the name #{name} of a different type (#{LV::names_table[name].class}) already exists") if LV::names_table["#{name}"]
5-
LV::names_table["#{name}"] = self
3+
def initialize(name, args)
64
@name = name
5+
@args = args
6+
raise StandardError.new("Variable with the name #{self} of a different type (#{LV::names_table[self.to_s].class}) already exists") if LV::names_table[self.to_s]
7+
LV::names_table[self.to_s] = self
78
end
89

910
def self.included(base)
@@ -21,7 +22,7 @@ def clear
2122
end
2223

2324
def to_s
24-
"#{self.name}"
25+
"#{self.name}#{self.args.join("_")}"
2526
end
2627
end
2728
end

lib/solvers/cbc.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def store_results(variables)
1717
vars_by_name[cols[1].to_s] = cols[2].to_f
1818
end
1919
variables.each do |var|
20-
var.value = vars_by_name[var.name.to_s].to_f
20+
var.value = vars_by_name[var.to_s].to_f
2121
end
2222
return objective
2323
end

lib/solvers/scip.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def store_results(variables)
2323
vars_by_name[cols[0].to_s] = cols[1].to_f
2424
end
2525
variables.each do |var|
26-
var.value = vars_by_name[var.name.to_s].to_f
26+
var.value = vars_by_name[var.to_s].to_f
2727
end
2828

2929
return objective

rulp.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Gem::Specification.new do |s|
22
s.name = 'rulp'
3-
s.version = '0.0.5'
3+
s.version = '0.0.6'
44
s.date = Date.today
55
s.summary = "Ruby Linear Programming"
66
s.description = "A simple Ruby LP description DSL"

0 commit comments

Comments
 (0)