amor 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,275 @@
1
+ require 'amor/expression'
2
+
3
+ module Amor
4
+ describe Expression do
5
+ before(:each) do
6
+ @variable = double('Variable')
7
+ @expression = Expression.new([[2, @variable]])
8
+ end
9
+
10
+ describe '.new' do
11
+ context 'when used with another Expression' do
12
+ before(:each) do
13
+ @other_variable = double('Variable')
14
+ @other_expression = Expression.new([[3.0, @other_variable]])
15
+ end
16
+
17
+ it 'Copys the Expression' do
18
+ expect(Expression.new(@other_expression).factors).to eq(@other_expression.factors)
19
+ end
20
+ end
21
+
22
+ context 'when used with an Array of factors' do
23
+ before(:each) do
24
+ @factors = [[3.0, double('Variable')]]
25
+ end
26
+
27
+ it 'Creates an Expression with the given factors' do
28
+ expect(Expression.new(@factors).factors).to eq(@factors)
29
+ end
30
+ end
31
+
32
+ context 'when used with a Variable' do
33
+ before(:each) do
34
+ @variable = Variable.new(double('Model'),1)
35
+ end
36
+
37
+ it 'Creates an Expression with the given variable as factor with scalar 1' do
38
+ expect(Expression.new(@variable).factors).to eq([[1, @variable]])
39
+ end
40
+ end
41
+
42
+ [-3, -3.0].each do |number|
43
+ context "when used with a #{number.class}" do
44
+ it 'Creates an Expression with the given number as a constant factor' do
45
+ expect(Expression.new(number).factors).to eq([[number, :constant]])
46
+ end
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ describe '#+' do
53
+ context 'when used with another Expression' do
54
+ before(:each) do
55
+ @other_variable = double('Other Variable')
56
+ @other_expression = Expression.new([[3.0, @other_variable]])
57
+ end
58
+
59
+ it 'returns an Expression' do
60
+ expect(@expression + @other_expression).to be_a(Expression)
61
+ end
62
+
63
+ it 'returns an Expression with the combined factors of both Expressions' do
64
+ expect((@expression + @other_expression).factors).to eq([[2, @variable], [3.0, @other_variable]])
65
+ end
66
+ end
67
+
68
+ context 'when used with a variable' do
69
+ before(:each) do
70
+ @other_variable = Variable.new(double('Model'),1)
71
+ end
72
+
73
+ it 'returns an Expression' do
74
+ expect(@expression + @other_variable).to be_a(Expression)
75
+ end
76
+
77
+ it 'returns an Expression with the factor of the variable set to 1' do
78
+ expect((@expression + @other_variable).factors).to eq([[2, @variable], [1, @other_variable]])
79
+ end
80
+ end
81
+
82
+ [4, 4.0].each do |number|
83
+ context "when used with a #{number.class}" do
84
+ it 'returns an Expression' do
85
+ expect(@expression + number).to be_a(Expression)
86
+ end
87
+
88
+ it 'returns an Expression with the the last factor set to the corresponding constant' do
89
+ expect((@expression + number).factors).to eq([[2, @variable], [number, :constant]])
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ describe '#-' do
96
+ context 'when used with another Expression' do
97
+ before(:each) do
98
+ @other_variable = double('Other Variable')
99
+ @other_expression = Expression.new([[3.0, @other_variable]])
100
+ end
101
+
102
+ it 'returns an Expression' do
103
+ expect(@expression - @other_expression).to be_a(Expression)
104
+ end
105
+
106
+ it 'returns an Expression with the combined factors of the first Expression and the negative second Expression' do
107
+ expect((@expression - @other_expression).factors).to eq([[2, @variable], [-3.0, @other_variable]])
108
+ end
109
+ end
110
+
111
+ context 'when used with a variable' do
112
+ before(:each) do
113
+ @other_variable = Variable.new(double('Model'),1)
114
+ end
115
+
116
+ it 'returns an Expression' do
117
+ expect(@expression - @other_variable).to be_a(Expression)
118
+ end
119
+
120
+ it 'returns an Expression with the factor of the variable set to -1' do
121
+ expect((@expression - @other_variable).factors).to eq([[2, @variable], [-1, @other_variable]])
122
+ end
123
+ end
124
+
125
+ [4, 4.0].each do |number|
126
+ context "when used with a #{number.class}" do
127
+ it 'returns an Expression' do
128
+ expect(@expression - number).to be_a(Expression)
129
+ end
130
+
131
+ it 'returns an Expression with the the last factor set to the corresponding negative constant' do
132
+ expect((@expression - number).factors).to eq([[2, @variable], [-number, :constant]])
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ describe '#-@' do
139
+ it 'returns an Expression' do
140
+ expect(-@expression).to be_a(Expression)
141
+ end
142
+
143
+ it 'returns an Expression with the negative factors' do
144
+ expect((-@expression).factors).to eq([[-2, @variable]])
145
+ end
146
+ end
147
+
148
+ describe '#<=' do
149
+ context 'when used with another Expression' do
150
+ before(:each) do
151
+ @other_expression = Expression.new(5.0)
152
+ end
153
+
154
+ it 'returns a Constraint' do
155
+ expect(@expression <= @other_expression).to be_a(Constraint)
156
+ end
157
+
158
+ it 'sets the Constraints relation to :lesser_equal' do
159
+ expect((@expression <= @other_expression).relation).to eq(:lesser_equal)
160
+ end
161
+
162
+ it 'sets the lhs of the Constraint to self' do
163
+ expect((@expression <= @other_expression).lhs).to eql(@expression)
164
+ end
165
+
166
+ it 'sets the rhs of the Constraint to the other Expression' do
167
+ expect((@expression <= @other_expression).rhs).to eql(@other_expression)
168
+ end
169
+ end
170
+ end
171
+
172
+ describe '#>=' do
173
+ context 'when used with another Expression' do
174
+ before(:each) do
175
+ @other_expression = Expression.new(5.0)
176
+ end
177
+
178
+ it 'returns a Constraint' do
179
+ expect(@expression >= @other_expression).to be_a(Constraint)
180
+ end
181
+
182
+ it 'sets the Constraints relation to :greater_equal' do
183
+ expect((@expression >= @other_expression).relation).to eq(:greater_equal)
184
+ end
185
+
186
+ it 'sets the lhs of the Constraint to self' do
187
+ expect((@expression >= @other_expression).lhs).to eql(@expression)
188
+ end
189
+
190
+ it 'sets the rhs of the Constraint to the other Expression' do
191
+ expect((@expression >= @other_expression).rhs).to eql(@other_expression)
192
+ end
193
+ end
194
+ end
195
+
196
+ describe '#==' do
197
+ context 'when used with another Expression' do
198
+ before(:each) do
199
+ @other_expression = Expression.new(5.0)
200
+ end
201
+
202
+ it 'returns a Constraint' do
203
+ expect(@expression == @other_expression).to be_a(Constraint)
204
+ end
205
+
206
+ it 'sets the Constraints relation to :greater_equal' do
207
+ expect((@expression == @other_expression).relation).to eq(:equal)
208
+ end
209
+
210
+ it 'sets the lhs of the Constraint to self' do
211
+ expect((@expression == @other_expression).lhs).to eql(@expression)
212
+ end
213
+
214
+ it 'sets the rhs of the Constraint to the other Expression' do
215
+ expect((@expression == @other_expression).rhs).to eql(@other_expression)
216
+ end
217
+ end
218
+ end
219
+
220
+ describe '#simplified' do
221
+ it 'returns an Expression' do
222
+ expect(Expression.new(1).simplified).to be_a(Expression)
223
+ end
224
+
225
+ it 'returns the Expression with no dupplications in the factors' do
226
+ v1 = double('Variable')
227
+ v2 = double('Variable')
228
+ v3 = double('Variable')
229
+ factors = Expression.new([[3, v1], [-2.0, v2], [-1, v1], [2, :constant], [2.5, v3], [1, v2], [-2.0, :constant], [3, :constant], [-2.5, v3]]).simplified.factors
230
+ expect(factors).to include([2, v1])
231
+ expect(factors).to include([-1.0, v2])
232
+ expect(factors).to include([3.0, :constant])
233
+ end
234
+
235
+ it 'returns an Expression where no factors with scalar 0 appear' do
236
+ v1 = double('Variable')
237
+ v2 = double('Variable')
238
+ v3 = double('Variable')
239
+ expect(Expression.new([[3, v1], [-2.0, v2], [-1, v1], [2, :constant], [2.5, v3], [1, v2], [-2.0, :constant], [3, :constant], [-2.5, v3]]).simplified.factors.flatten).not_to include(v3)
240
+ end
241
+ end
242
+
243
+ describe '#remove_constants' do
244
+ it 'returns an Expression containing all but the constant factors' do
245
+ model = Model.new
246
+ v1 = model.x(1)
247
+ v2 = model.x(2)
248
+ v3 = model.x(3)
249
+ factors = Expression.new([[3, v1], [-2.0, v2], [-1, v1], [2, :constant], [2.5, v3], [1, v2], [-2.0, :constant], [3, :constant], [-2.5, v3]]).remove_constants.factors
250
+ expect(factors).to eq([[3, v1], [-2.0, v2], [-1, v1], [2.5, v3], [1, v2], [-2.5, v3]])
251
+ end
252
+ end
253
+
254
+ describe '#constant_factor' do
255
+ it 'returns the total constant factor' do
256
+ model = Model.new
257
+ v1 = model.x(1)
258
+ v2 = model.x(2)
259
+ v3 = model.x(3)
260
+ expect(Expression.new([[3, v1], [-2.0, v2], [-1, v1], [2, :constant], [2.5, v3], [1, v2], [-2.0, :constant], [3, :constant], [-2.5, v3]]).constant_factor).to eq(3.0)
261
+ end
262
+ end
263
+
264
+ describe '#lp_string' do
265
+ it 'returns a string representation feasible for the LP file format' do
266
+ model = Model.new
267
+ v1 = model.x(1)
268
+ v2 = model.x(2)
269
+ v3 = model.x(3)
270
+ expect(Expression.new([[3, v1], [-2.0, v2], [-1, v1], [2, :constant], [2.5, v3], [1, v2], [-2.0, :constant], [3, :constant], [-2.5, v3]]).lp_string).to eq('2 x1 - 1.0 x2 + 3.0')
271
+ end
272
+ end
273
+
274
+ end
275
+ end
@@ -0,0 +1,120 @@
1
+ require 'amor/model'
2
+
3
+ module Amor
4
+ describe Model do
5
+ before(:each) do
6
+ @model = Model.new
7
+ end
8
+
9
+ describe '#x' do
10
+ it 'returns a variable' do
11
+ expect(@model.x(1)).to be_a(Variable)
12
+ end
13
+
14
+ it 'returns the same variable for the same indices' do
15
+ expect(@model.x(1)).to equal(@model.x(1))
16
+ end
17
+
18
+ it 'returns different variables for different indices' do
19
+ expect(@model.x(1)).not_to equal(@model.x(2))
20
+ end
21
+
22
+ it 'stores the model in the variable' do
23
+ expect(@model.x(1).model).to equal(@model)
24
+ end
25
+
26
+ it 'stores the index in the variable' do
27
+ expect(@model.x(1).index).to equal(1)
28
+ end
29
+ end
30
+
31
+ describe '#min' do
32
+ before(:each) do
33
+ @expression = Expression.new([[3, double('Variable')], [-2, double('Variable')]])
34
+ end
35
+
36
+ it 'returns an Objective' do
37
+ expect(@model.min(@expression)).to be_a(Objective)
38
+ end
39
+
40
+ it 'returns an Objective with the direction set to :min' do
41
+ expect(@model.min(@expression).direction).to eq(:minimize)
42
+ end
43
+
44
+ it 'returns an Objective with the expression set to the given Expression' do
45
+ expect(@model.min(@expression).expression).to eql(@expression)
46
+ end
47
+
48
+ it 'stores the objective in the model' do
49
+ objective = @model.min(@expression)
50
+ expect(@model.objective).to equal(objective)
51
+ end
52
+ end
53
+
54
+ describe '#max' do
55
+ before(:each) do
56
+ @expression = Expression.new([[3, double('Variable')], [-2, double('Variable')]])
57
+ end
58
+
59
+ it 'returns an Objective' do
60
+ expect(@model.max(@expression)).to be_a(Objective)
61
+ end
62
+
63
+ it 'returns an Objective with the direction set to :max' do
64
+ expect(@model.max(@expression).direction).to eq(:maximize)
65
+ end
66
+
67
+ it 'returns an Objective with the expression set to the given Expression' do
68
+ expect(@model.max(@expression).expression).to eql(@expression)
69
+ end
70
+
71
+ it 'stores the objective in the model' do
72
+ objective = @model.max(@expression)
73
+ expect(@model.objective).to equal(objective)
74
+ end
75
+ end
76
+
77
+ describe '#st' do
78
+ before(:each) do
79
+ @constraint = Constraint.new(1, :greater_equal, Variable.new(double('Model'), 1))
80
+ end
81
+
82
+ it 'returns the given Constraint' do
83
+ expect(@model.st(@constraint)).to equal(@constraint)
84
+ end
85
+
86
+ it 'stores the constraint in the model' do
87
+ @model.st(@constraint)
88
+ expect(@model.constraints).to include(@constraint)
89
+ end
90
+ end
91
+
92
+
93
+ describe '.from_string' do
94
+ before(:each) do
95
+ @string = "min x(3) + 3 * x(2) + 4.0\nst x(2) - 2.0 * x(3) <= 5.0"
96
+ end
97
+
98
+ it 'returns a Model' do
99
+ expect(Model.from_string(@string)).to be_a(Model)
100
+ end
101
+
102
+ it 'returns a Model with the specified objective' do
103
+ model = Model.from_string(@string)
104
+ expect(model.objective.expression.factors).to eq([[1, model.x(3)], [3, model.x(2)], [4.0, :constant]])
105
+ end
106
+
107
+ it 'returns a Model with the specified constraint' do
108
+ model = Model.from_string(@string)
109
+ expect(model.constraints[0].lhs.factors).to eq([[1, model.x(2)], [-2.0, model.x(3)]])
110
+ end
111
+ end
112
+
113
+ describe '.lp_string' do
114
+ it 'returns a LP Format ready string of the model' do
115
+ model = Model.from_string("min x(3) + 3 * x(2) + 4.0\nst x(2) - 2.0 * x(3) <= 5.0")
116
+ expect(model.lp_string).to eq("Minimize\n obj: 1 x1 + 3 x2 + 4.0\nSubject To\n c1: 1 x2 - 2.0 x1 <= 5.0\nEnd")
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,83 @@
1
+ require 'amor/numeric_extensions'
2
+
3
+ module Amor
4
+ [5, 5.0].each do |number|
5
+ describe "#{number.class}" do
6
+ describe '#*' do
7
+ context 'when used with a variable' do
8
+ before(:each) do
9
+ @variable = Variable.new(double('Model'), 1)
10
+ end
11
+
12
+ it 'returns an Expression' do
13
+ expect(number * @variable).to be_a(Expression)
14
+ end
15
+
16
+ it 'returns an Expression with the factor of the variable set to the number' do
17
+ expect((number * @variable).factors).to eq([[number, @variable]])
18
+ end
19
+ end
20
+ end
21
+
22
+ describe '#+' do
23
+ context 'when used with an expression' do
24
+ before(:each) do
25
+ @variable = double('Variable')
26
+ @expression = Expression.new([[2.0, @variable]])
27
+ end
28
+
29
+ it 'returns an Expression' do
30
+ expect(number + @expression).to be_a(Expression)
31
+ end
32
+
33
+ it 'returns an Expression with the first factor set to the constant' do
34
+ expect((number + @expression).factors).to eq([[number, :constant], [2.0, @variable]])
35
+ end
36
+ end
37
+
38
+ context 'when used with a variable' do
39
+ before(:each) do
40
+ @variable = Variable.new(double('Model'), 1)
41
+ end
42
+ it 'returns an Expression' do
43
+ expect(number + @variable).to be_a(Expression)
44
+ end
45
+
46
+ it 'returns an Expression with the first factor set to the constant and the second to the variable with scalar 1' do
47
+ expect((number + @variable).factors).to eq([[number, :constant], [1, @variable]])
48
+ end
49
+ end
50
+ end
51
+
52
+ describe '#-' do
53
+ context 'when used with an expression' do
54
+ before(:each) do
55
+ @variable = double('Variable')
56
+ @expression = Expression.new([[2.0, @variable]])
57
+ end
58
+
59
+ it 'returns an Expression' do
60
+ expect(number - @expression).to be_a(Expression)
61
+ end
62
+
63
+ it 'returns an Expression with the first factor set to the constant and the second to the negative Expression' do
64
+ expect((number - @expression).factors).to eq([[number, :constant], [-2.0, @variable]])
65
+ end
66
+ end
67
+
68
+ context 'when used with a variable' do
69
+ before(:each) do
70
+ @variable = Variable.new(double('Model'), 1)
71
+ end
72
+ it 'returns an Expression' do
73
+ expect(number - @variable).to be_a(Expression)
74
+ end
75
+
76
+ it 'returns an Expression with the first factor set to the constant and the second to the variable with scalar -1' do
77
+ expect((number - @variable).factors).to eq([[number, :constant], [-1, @variable]])
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,14 @@
1
+ require 'amor/objective'
2
+
3
+ module Amor
4
+ describe Objective do
5
+ describe '#lp_string' do
6
+ it 'returns LP format ready version of objective, starting with Maximize if direction is :maximize' do
7
+ expect(Objective.new(:maximize, Expression.new(2)).lp_string).to eq("Maximize\n obj: 2")
8
+ end
9
+ it 'returns LP format ready version of objective, starting with Minimize if direction is :minimize' do
10
+ expect(Objective.new(:minimize, Expression.new(2)).lp_string).to eq("Minimize\n obj: 2")
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,140 @@
1
+ require 'amor/variable'
2
+
3
+ module Amor
4
+ describe Variable do
5
+ before(:each) do
6
+ @model = double('Model')
7
+ @variable = Variable.new(@model, 1)
8
+ end
9
+
10
+ describe '#*' do
11
+ # Make sure it works with Integer and Float
12
+ [5, 5.0].each do |number|
13
+ context "used with a #{number.class} as scalar" do
14
+ it "returns an Expression when called with #{number}" do
15
+ expect(@variable * number).to be_a(Expression)
16
+ end
17
+
18
+ it "returns an Expression with one factor" do
19
+ expect((@variable * number).factors.size).to eq(1)
20
+ end
21
+
22
+ it "returns an Expression with one factor consisting of given variable and the value it was called with" do
23
+ expect((@variable * number).factors.first).to eq([number, @variable])
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ describe '#+' do
30
+ context 'when used with an expression' do
31
+ before(:each) do
32
+ @other_variable = double('Other Variable')
33
+ @other_expression = Expression.new([[3.0, @other_variable]])
34
+ end
35
+
36
+ it 'returns an Expression' do
37
+ expect(@variable + @other_expression).to be_a(Expression)
38
+ end
39
+
40
+ it 'returns an Expression with the factor of the variable set to 1' do
41
+ expect((@variable + @other_expression).factors).to eq([[1, @variable], [3.0, @other_variable]])
42
+ end
43
+ end
44
+
45
+ context 'when used with a variable' do
46
+ before(:each) do
47
+ @other_variable = Variable.new(@model,2)
48
+ end
49
+
50
+ it 'returns an Expression' do
51
+ expect(@variable + @other_variable).to be_a(Expression)
52
+ end
53
+
54
+ it 'returns an Expression with the factor of the variables set to 1' do
55
+ expect((@variable + @other_variable).factors).to eq([[1, @variable], [1, @other_variable]])
56
+ end
57
+ end
58
+
59
+ [6, 6.0].each do |number|
60
+ context "when used with a #{number.class}" do
61
+ it 'returns an Expression' do
62
+ expect(@variable + number).to be_a(Expression)
63
+ end
64
+
65
+ it 'returns an Expression with the first factor set to the variable and the second factor set to the constant' do
66
+ expect((@variable + number).factors).to eq([[1, @variable], [number, :constant]])
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ describe '#-' do
73
+ context 'when used with an expression' do
74
+ before(:each) do
75
+ @other_variable = double('Other Variable')
76
+ @other_expression = Expression.new([[3.0, @other_variable]])
77
+ end
78
+
79
+ it 'returns an Expression' do
80
+ expect(@variable - @other_expression).to be_a(Expression)
81
+ end
82
+
83
+ it 'returns an Expression with the factor of the variable set to 1 and the negative expression' do
84
+ expect((@variable - @other_expression).factors).to eq([[1, @variable], [-3.0, @other_variable]])
85
+ end
86
+ end
87
+
88
+ context 'when used with a variable' do
89
+ before(:each) do
90
+ @other_variable = Variable.new(@model,2)
91
+ end
92
+
93
+ it 'returns an Expression' do
94
+ expect(@variable - @other_variable).to be_a(Expression)
95
+ end
96
+
97
+ it 'returns an Expression with the factor of the first variable set to 1 and the second to -1' do
98
+ expect((@variable - @other_variable).factors).to eq([[1, @variable], [-1, @other_variable]])
99
+ end
100
+ end
101
+
102
+ [6, 6.0].each do |number|
103
+ context "when used with a #{number.class}" do
104
+ it 'returns an Expression' do
105
+ expect(@variable - number).to be_a(Expression)
106
+ end
107
+
108
+ it 'returns an Expression with the first factor set to the variable and the second factor set to the negative constant' do
109
+ expect((@variable - number).factors).to eq([[1, @variable], [-number, :constant]])
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ describe '#-@' do
116
+ it 'returns an Expression' do
117
+ expect(-@variable).to be_a(Expression)
118
+ end
119
+
120
+ it 'returns an Expression with the variables factor set to -1' do
121
+ expect((-@variable).factors).to eq([[-1, @variable]])
122
+ end
123
+ end
124
+
125
+ describe '#internal_index' do
126
+ before(:each) do
127
+ @model = Model.new
128
+ end
129
+
130
+ it 'returns 0 for the first variable' do
131
+ expect(@model.x(1).internal_index).to eq(0)
132
+ end
133
+
134
+ it 'returns 1 for the second variable' do
135
+ @model.x(1)
136
+ expect(@model.x(2).internal_index).to eq(1)
137
+ end
138
+ end
139
+ end
140
+ end