amor 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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