Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(): Change auto create variables when defined variables which st… #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 45 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,25 +59,25 @@ Rulp is inspired by the ruby wrapper for the GLPK toolkit and the python LP libr

given[

X_i >= 0,
Y_i >= 0,
Z_i >= 0
X_lpi >= 0,
Y_lpi >= 0,
Z_lpi >= 0

]

result = Rulp::Max( 10 * X_i + 6 * Y_i + 4 * Z_i ) [
X_i + Y_i + Z_i <= 100,
10 * X_i + 4 * Y_i + 5 * Z_i <= 600,
2 * X_i + 2 * Y_i + 6 * Z_i <= 300
result = Rulp::Max( 10 * X_lpi + 6 * Y_lpi + 4 * Z_lpi ) [
X_lpi + Y_lpi + Z_lpi <= 100,
10 * X_lpi + 4 * Y_lpi + 5 * Z_lpi <= 600,
2 * X_lpi + 2 * Y_lpi + 6 * Z_lpi <= 300
].solve

##
# 'result' is the result of the objective function.
# You can retrieve the values of variables by using the 'value' method
# E.g
# X_i.value == 32
# Y_i.value == 67
# Z_i.value == 0
# X_lpi.value == 32
# Y_lpi.value == 67
# Z_lpi.value == 0
##
```

Expand Down Expand Up @@ -189,18 +189,18 @@ At this point, you should have GLPK installed. Verify it:
# need to initialize them.
# They follow a naming convention that defines their type.
# A variable is declared as a constant with one of three different suffixes.
# 'f' or '_f' indicates a general variable (No constraints)
# 'i' or '_i' indicates a integer variable
# 'b' or '_b' indicates a binary/boolean variable
# 'lpf' or '_lpf' indicates a general variable (No constraints)
# 'lpi' or '_lpi' indicates a integer variable
# 'lpb' or '_lpb' indicates a binary/boolean variable


An_Integer_i
An_Integer_lpi
=> #<IV:0x007ffc4b651b80 @name="An_Integer">

Generalf
Generallpf
=> #<LV:0x007ffc4b651b80 @name="General">

Bool_Val_b
Bool_Val_lpb
=> #<BV:0x007ffc4b67b6b0 @name="Bool_Val">

# In some cases it is implausible to generate a unique name for every possible variable
Expand All @@ -209,13 +209,13 @@ At this point, you should have GLPK installed. Verify it:
# accept index parameters to create large ranges of unique variables.
# Examples of how indexed variables can be declared are as follows:

Item_i(4,5)
Item_lpi(4,5)
#<IV:0x007ffc4b3ea518 @name="Item4_5">

Item_i("store_3", "table_2")
Item_lpi("store_3", "table_2")
#<IV:0x007ffc4b3a3cd0 @name="Itemstore_3_table_2">

[*0..10].map(&Unit_f)
[*0..10].map(&Unit_lpf)
=> [#<LV:0x007ffc4cc25768 @name="Unit0">,
#<LV:0x007ffc4cc24cf0 @name="Unit1">,
#<LV:0x007ffc4cc0fc88 @name="Unit2">,
Expand All @@ -237,16 +237,16 @@ Constraints on a variable can only use numeric literals and not other variables.
Inter-variable constraints should be expressed as problem constrants. (Explained below.)

```ruby
X_i < 5
X_i.bounds
X_lpi < 5
X_lpi.bounds
=> "X <= 5"

3 <= X_i < 15
3 <= X_lpi < 15
X_i.bounds
=> "3 <= X <= 15"

Y_f == 10
Y_f.bounds
Y_lpf == 10
Y_lpf.bounds
=> "y = 10"
```

Expand All @@ -255,14 +255,14 @@ Inter-variable constraints should be expressed as problem constrants. (Explained
Constraints are added to a problem using the :[] syntax.

```ruby
problem = Rulp::Max( 10 * X_i + 6 * Y_i + 4 * Z_i )
problem = Rulp::Max( 10 * X_lpi + 6 * Y_lpi + 4 * Z_lpi )

problem[
X_i + Y_i + Z_i <= 100
X_lpi + Y_lpi + Z_lpi <= 100
]

problem[
10 * X_i + 4 * Y_i + 5 * Z_i <= 600
10 * X_lpi + 4 * Y_lpi + 5 * Z_lpi <= 600
]
...
problem.solve
Expand All @@ -271,10 +271,10 @@ Constraints are added to a problem using the :[] syntax.
You can add multiple constraints at once by comma separating them as seen in the earlier examples:

```ruby
Rulp::Max( 10 * X_i + 6 * Y_i + 4 * Z_i ) [
X_i + Y_i + Z_i <= 100,
10 * X_i + 4 * Y_i + 5 * Z_i <= 600,
2 * X_i + 2 * Y_i + 6 * Z_i <= 300
Rulp::Max( 10 * X_lpi + 6 * Y_lpi + 4 * Z_lpi ) [
X_lpi + Y_lpi + Z_lpi <= 100,
10 * X_lpi + 4 * Y_lpi + 5 * Z_lpi <= 600,
2 * X_lpi + 2 * Y_lpi + 6 * Z_lpi <= 300
]
```

Expand All @@ -288,10 +288,10 @@ such that the command `which [exec_name]` returns a path. (I.e they must be on y
Given a problem there are multiple ways to initiate a solver.

```ruby
@problem = Rulp::Max( 10 * X_i + 6 * Y_i + 4 * Z_i ) [
X_i + Y_i + Z_i <= 100,
10 * X_i + 4 * Y_i + 5 * Z_i <= 600,
2 * X_i + 2 * Y_i + 6 * Z_i <= 300
@problem = Rulp::Max( 10 * X_lpi + 6 * Y_lpi + 4 * Z_lpi ) [
X_lpi + Y_lpi + Z_lpi <= 100,
10 * X_lpi + 4 * Y_lpi + 5 * Z_lpi <= 600,
2 * X_lpi + 2 * Y_lpi + 6 * Z_lpi <= 300
]
```

Expand Down Expand Up @@ -410,13 +410,13 @@ You can then play with and attempt LP and MIP problems straight from the command

```ruby

[1] pry(main)> 13 <= X_i <= 45 # Declare integer variable
[1] pry(main)> 13 <= X_lpi <= 45 # Declare integer variable
=> X(i)[undefined]

[2] pry(main)> -15 <= Y_f <= 15 # Declare float variable
[2] pry(main)> -15 <= Y_lpf <= 15 # Declare float variable
=> Y(f)[undefined]

[3] pry(main)> @problem = Rulp::Min(X_i - Y_f) # Create min problem
[3] pry(main)> @problem = Rulp::Min(X_lpi - Y_lpf) # Create min problem
[info] Creating minimization problem
=>
Minimize
Expand All @@ -430,7 +430,7 @@ You can then play with and attempt LP and MIP problems straight from the command
X
End

[4] @problem[ X_i - 2 * Y_f < 40] #Adding a problem constraint
[4] @problem[ X_lpi - 2 * Y_lpf < 40] #Adding a problem constraint
=>
Minimize
obj: X -Y
Expand All @@ -450,21 +450,21 @@ You can then play with and attempt LP and MIP problems straight from the command
[info] Parsing result
=> -2.0 #(Minimal result)

[6] pry(main)> Y_f # See value calculated for Y now that solver has run
[6] pry(main)> Y_lpf # See value calculated for Y now that solver has run
=> Y(f)[15.0]

[8] pry(main)> X_i
[8] pry(main)> X_lpi
=> X(i)[13.0]

# The result of the objective function (-2.0) was returned by the call to .solve
# Now the solver has run and calculated values for our variables we can also test the
# objective function (or any function) by calling evaluate on it.
# E.g

[9] (X_i - Y_f).evaluate
[9] (X_lpi - Y_lpf).evaluate
=> -2.0

[10] pry(main)> (2 * X_i + 15 * Y_f).evaluate
[10] pry(main)> (2 * X_lpi + 15 * Y_lpf).evaluate
=> 251.0
```

Expand All @@ -481,7 +481,7 @@ we don't. We can't partially purchase one.)
```ruby
# Generate the data randomly for this example.
costs, points = [*0..1000].map do |i|
[Purchase_b(i) * Random.rand(1.0..3.0), Purchase_b(i) * Random.rand(5.0..10.0)]
[Purchase_lpb(i) * Random.rand(1.0..3.0), Purchase_lpb(i) * Random.rand(5.0..10.0)]
end.transpose.map(&:sum) #We sum the array of points and array of costs to create a Rulp expression

# And this is where the magic happens!. We ask rulp to maximise the number of points given
Expand All @@ -493,7 +493,7 @@ Rulp::Max(points)[
=> 538.2125623353652 (# You will get a different value as data was generated randomly)

# Now how do we check which purchases were selected?
selected_purchases = [*0..1000].map(&Purchase_b).select(&:selected?)
selected_purchases = [*0..1000].map(&Purchase_lpb).select(&:selected?)

=> [Purchase27(b)[true],
Purchase86(b)[true],
Expand Down
6 changes: 3 additions & 3 deletions examples/boolean_example.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#
##

items = 50.times.map(&Shop_Item_b)
items = 50.times.map(&Shop_Item_lpb)
items_count = items.sum
items_costs = items.map{|item| item * Random.rand(1.0...5.0)}.sum

Expand All @@ -25,5 +25,5 @@
# 'cost' is the result of the objective function.
# You can retrieve allocations by querying the variables.
# E.g
# Shop_Item_b(4).value
##
# Shop_Item_lpb(4).value
##
22 changes: 11 additions & 11 deletions examples/simple_example.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@

given[

X_i >= 0,
Y_i >= 0,
Z_i >= 0
X_lpi >= 0,
Y_lpi >= 0,
Z_lpi >= 0

]

Rulp::Max( objective = 10 * X_i + 6 * Y_i + 4 * Z_i ) [
X_i + Y_i + Z_i <= 100,
10 * X_i + 4 * Y_i + 5 * Z_i <= 600,
2 * X_i + 2 * Y_i + 6 * Z_i <= 300
Rulp::Max( objective = 10 * X_lpi + 6 * Y_lpi + 4 * Z_lpi ) [
X_lpi + Y_lpi + Z_lpi <= 100,
10 * X_lpi + 4 * Y_lpi + 5 * Z_lpi <= 600,
2 * X_lpi + 2 * Y_lpi + 6 * Z_lpi <= 300
].cbc

result = objective.evaluate
Expand All @@ -36,7 +36,7 @@
# 'result' is the result of the objective function.
# You can retrieve the values of variables by using the 'value' method
# E.g
# X_i.value == 32
# Y_i.value == 67
# Z_i.value == 0
##
# X_lpi.value == 32
# Y_lpi.value == 67
# Z_lpi.value == 0
##
14 changes: 7 additions & 7 deletions examples/whiskas_model2.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#
require_relative "../lib/rulp"

ingredients = [Chicken_i, Beef_i, Mutton_i, Rice_i, Wheat_i, Gel_i]
ingredients = [Chicken_lpi, Beef_lpi, Mutton_lpi, Rice_lpi, Wheat_lpi, Gel_lpi]
costs = {Chicken: 0.013, Beef: 0.008, Mutton: 0.010, Rice: 0.002, Wheat: 0.005, Gel: 0.001}
protein_percent = {Chicken: 0.100, Beef: 0.200, Mutton: 0.150, Rice: 0.000, Wheat: 0.040, Gel: 0.000}
fat_percent = {Chicken: 0.080, Beef: 0.100, Mutton: 0.110, Rice: 0.010, Wheat: 0.010, Gel: 0.000}
Expand All @@ -26,9 +26,9 @@

puts "Total Cost Per Can: #{result}"
puts
puts "Chicken: #{Chicken_i.value}"
puts "Beef: #{Beef_i.value}"
puts "Mutton: #{Mutton_i.value}"
puts "Rice: #{Rice_i.value}"
puts "Wheat: #{Wheat_i.value}"
puts "Gel: #{Gel_i.value}"
puts "Chicken: #{Chicken_lpi.value}"
puts "Beef: #{Beef_lpi.value}"
puts "Mutton: #{Mutton_lpi.value}"
puts "Rice: #{Rice_lpi.value}"
puts "Wheat: #{Wheat_lpi.value}"
puts "Gel: #{Gel_lpi.value}"
20 changes: 10 additions & 10 deletions lib/extensions/kernel_extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
# This is similar to the syntax defined in the object extensions but allows for numbered
# suffixes to quickly generate ranges of similar variables.
#
# Where lp var type suffix is either _b for binary, _i for integer, or _f for float.
# Where lp var type suffix is either _lpb for binary, _lpi for integer, or _lpf for float.
# I.e
#
# Rating_i(5) is the equivalent of Rating_5 (type integer)
# Is_happy_b(2) is the equivalent of Is_happy_2 (type binary/boolean)
# Rating_lpi(5) is the equivalent of Rating_5 (type integer)
# Is_happy_lpb(2) is the equivalent of Is_happy_2 (type binary/boolean)
# ...
##
module Kernel
Expand All @@ -20,15 +20,15 @@ def method_missing(value, *args)
method_name = "#{value}" rescue ""
start = method_name[0]
if (start <= "Z" && start >= "A")
case method_name[-1]
when "b"
method_name = method_name[0..(method_name[-2] == "_" ? -3 : -2)]
case method_name[-3..-1]
when "lpb"
method_name = method_name[0..(method_name[-4] == "_" ? -5 : -4)]
return BV.definition(method_name, args)
when "i"
method_name = method_name[0..(method_name[-2] == "_" ? -3 : -2)]
when "lpi"
method_name = method_name[0..(method_name[-4] == "_" ? -5 : -4)]
return IV.definition(method_name, args)
when "f"
method_name = method_name[0..(method_name[-2] == "_" ? -3 : -2)]
when "lpf"
method_name = method_name[0..(method_name[-4] == "_" ? -5 : -4)]
return LV.definition(method_name, args)
end
end
Expand Down
20 changes: 10 additions & 10 deletions lib/extensions/object_extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@
#
# [Capitalized_varname][lp var type suffix]
#
# Where lp var type suffix is either _b for binary, _i for integer, or _f for float.
# Where lp var type suffix is either _lpb for binary, _lpi for integer, or _lpf for float.
# I.e
#
# Rating_i is the equivalent of Rating (type integer)
# Is_happy_b is the equivalent of Is_happy (type binary/boolean)
# Rating_lpi is the equivalent of Rating (type integer)
# Is_happy_lpb is the equivalent of Is_happy (type binary/boolean)
##
class << Object
alias_method :old_const_missing, :const_missing
def const_missing(value)
method_name = "#{value}".split("::")[-1] rescue ""
if (("A".."Z").include?(method_name[0]))
if(method_name.end_with?("b"))
return BV.definition(method_name[0..(method_name[-2] == "_" ? -3 : -2)])
elsif(method_name.end_with?("i"))
return IV.definition(method_name[0..(method_name[-2] == "_" ? -3 : -2)])
elsif(method_name.end_with?("f"))
return LV.definition(method_name[0..(method_name[-2] == "_" ? -3 : -2)])
if ("A".."Z").include?(method_name[0])
if(method_name.end_with?("lpb"))
return BV.definition(method_name[0..(method_name[-4] == "_" ? -5 : -4)])
elsif(method_name.end_with?("lpi"))
return IV.definition(method_name[0..(method_name[-4] == "_" ? -5 : -4)])
elsif(method_name.end_with?("lpf"))
return LV.definition(method_name[0..(method_name[-4] == "_" ? -5 : -4)])
end
end
old_const_missing(value)
Expand Down
8 changes: 4 additions & 4 deletions lib/rulp/lv.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def meth
end

def suffix
"f"
"lpf"
end

def self.definition(name, *args)
Expand Down Expand Up @@ -75,11 +75,11 @@ def inspect

class BV < LV;
def suffix
"b"
"lpb"
end
end
class IV < LV;
def suffix
"i"
"lpi"
end
end
end
Loading