cassowary-ruby 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,704 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ require "set"
4
+
5
+ module Cassowary
6
+ class SimplexSolver
7
+
8
+ attr_accessor :rows, :columns, :objective, :infeasible_rows,
9
+ :stay_plus_error_vars, :stay_minus_error_vars, :edit_vars,
10
+ :edit_constraints, :edit_plus_error_vars, :edit_minus_error_vars,
11
+ :prev_edit_constants, :new_edit_constants, :marker_vars,
12
+ :error_vars, :auto_solve
13
+
14
+ Epsilon = 1.0e-8
15
+
16
+ def add_bounds(var, lower = nil, upper = nil)
17
+ add_constraint lower.cn_leq(var) if lower
18
+ add_constraint var.cn_leq(upper) if upper
19
+ end
20
+
21
+ def add_constraint(constraint)
22
+ expr = make_expression(constraint)
23
+ unless try_adding_directly(expr)
24
+ add_with_artificial_variable(expr)
25
+ end
26
+ if auto_solve
27
+ optimize(objective)
28
+ set_external_variables
29
+ end
30
+ end
31
+
32
+ def remove_constraint(cn)
33
+ reset_stay_constants
34
+
35
+ # remove any error variables from the objective function
36
+ evars = error_vars.delete(cn) || []
37
+ zrow = objective
38
+ obj = rows[zrow]
39
+ evars.each do |v|
40
+ expr = rows[v]
41
+ if expr.nil?
42
+ obj.add_variable(v, cn.strength.symbolic_weight * -cn.weight, zrow, self)
43
+ else
44
+ obj.add_expression(expr, cn.strength.symbolic_weight * -cn.weight, zrow, self)
45
+ end
46
+ end
47
+
48
+ exit_var = nil
49
+ col = nil
50
+ min_ratio = 0
51
+
52
+ # try to make the marker variable basic, if it isn't already
53
+ marker = marker_vars.delete(cn)
54
+ unless rows.has_key? marker
55
+ # choose which variable to move out of the basis. only consider restricted basic vars
56
+ col = columns[marker]
57
+ col.each do |v|
58
+ if v.restricted?
59
+ expr = rows[v]
60
+ coeff = expr.coefficient_for(marker)
61
+ # only consider negative coefficients
62
+ if coeff < 0.0
63
+ r = 0.0 - expr.constant / coeff
64
+ if exit_var.nil? or r < min_ratio
65
+ min_ratio = r
66
+ exit_var = v
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ # If exitVar is still nil at this point, then either the
73
+ # marker variable has a positive coefficient in all equations,
74
+ # or it only occurs in equations for unrestricted variables.
75
+ # If it does occur in an equation for a restricted variable,
76
+ # pick the equation that gives the smallest ratio. (The row
77
+ # with the marker variable will become infeasible, but all the
78
+ # other rows will still be feasible; and we will be dropping
79
+ # the row with the marker variable. In effect we are removing
80
+ # the non-negativity restriction on the marker variable.)
81
+ if exit_var.nil?
82
+ col.each do |v|
83
+ if v.restricted?
84
+ expr = rows[v]
85
+ coeff = expr.coefficient_for(marker)
86
+ r = expr.constant / coeff
87
+ if exit_var.nil? or r < min_ratio
88
+ min_ratio = r
89
+ exit_var = v
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ # If exitVar is still nil, and col is empty, then exitVar
96
+ # doesn't occur in any equations, so just remove it.
97
+ # Otherwise pick an exit var from among the unrestricted
98
+ # variables whose equation involves the marker var
99
+ if exit_var.nil?
100
+ if col.empty?
101
+ remove_parametric_var(marker)
102
+ else
103
+ exit_var = col.to_a.first
104
+ end
105
+ end
106
+
107
+ if exit_var
108
+ pivot(marker, exit_var)
109
+ end
110
+ end
111
+
112
+ # Now delete any error variables. If cn is an inequality, it
113
+ # also contains a slack variable; but we use that as the
114
+ # marker variable and so it has been deleted when we removed
115
+ # its row
116
+ if rows.has_key?(marker)
117
+ remove_row(marker)
118
+ end
119
+ evars.each do |v|
120
+ remove_parametric_var(v) unless v == marker
121
+ end
122
+
123
+ if cn.stay_constraint?
124
+ self.stay_plus_error_vars = stay_plus_error_vars.reject do |v| evars.include? v end
125
+ self.stay_minus_error_vars = stay_minus_error_vars.reject do |v| evars.include? v end
126
+ end
127
+
128
+ if cn.edit_constraint?
129
+ # find the index in editPlusErrorVars of the error variable for this constraint
130
+ index = find_edit_error_index(evars)
131
+
132
+ # remove the error variables from editPlusErrorVars and editMinusErrorVars
133
+ edit_plus_error_vars.delete_at(index)
134
+ edit_minus_error_vars.delete_at(index)
135
+
136
+ # remove the constants from prevEditConstants
137
+ prev_edit_constants.delete_at(index)
138
+ end
139
+
140
+ if auto_solve
141
+ optimize(zrow)
142
+ set_external_variables
143
+ end
144
+ end
145
+
146
+ def resolve(cs = nil)
147
+ if cs
148
+ self.new_edit_constants = cs
149
+ end
150
+
151
+ # Re-solve the current collection of constraints for the new values in newEditConstants.
152
+ self.infeasible_rows = []
153
+ reset_stay_constants
154
+ reset_edit_constants
155
+ dual_optimize
156
+ set_external_variables
157
+ end
158
+
159
+ def solve
160
+ optimize objective
161
+ set_external_variables
162
+ end
163
+
164
+ def suggest_value(var, val)
165
+ edit_vars.each_with_index do |v, idx|
166
+ if v == var
167
+ new_edit_constants[idx] = val
168
+ end
169
+ return self
170
+ end
171
+ raise InternalError, "variable not currently being edited"
172
+ end
173
+
174
+ def add_edit_var(variable, strength)
175
+ add_constraint(EditConstraint.new variable: variable, strength: strength)
176
+ end
177
+
178
+ def add_stay(variable, strength = Strength::WeakStrength)
179
+ add_constraint(StayConstraint.new variable: variable, strength: strength)
180
+ end
181
+
182
+ def begin_edit
183
+ self.new_edit_constants = [nil] * edit_vars.size
184
+ end
185
+
186
+ def end_edit
187
+ edit_constraints.each do |cn|
188
+ remove_constraint(cn)
189
+ end
190
+ self.edit_vars = []
191
+ self.edit_constraints = []
192
+ end
193
+
194
+ def note_added_variable(var, subject)
195
+ if subject
196
+ columns[var] ||= Set.new
197
+ columns[var] << subject
198
+ end
199
+ end
200
+
201
+ def note_removed_variable(var, subject)
202
+ if subject
203
+ columns[var].delete(subject)
204
+ end
205
+ end
206
+
207
+ private
208
+ def add_row(var, expr)
209
+ rows[var] = expr
210
+ expr.each_variable_and_coefficient do |v, c|
211
+ columns[v] ||= Set.new
212
+ columns[v] << var
213
+ end
214
+ end
215
+
216
+ def add_with_artificial_variable(expr)
217
+ av = SlackVariable.new
218
+ az = ObjectiveVariable.new
219
+ azrow = LinearExpression.new
220
+
221
+ # the artificial objective is av, which we know is equal to expr
222
+ # (which contains only parametric variables)
223
+ azrow.constant = expr.constant
224
+ expr.each_variable_and_coefficient do |v, c|
225
+ azrow.terms[v] = c
226
+ end
227
+
228
+ add_row(az, azrow)
229
+ add_row(av, expr)
230
+
231
+ # try to optimize av to 0
232
+ optimize az
233
+
234
+ # Check that we were able to make the objective value 0. If
235
+ # not, the original constraint was unsatisfiable.
236
+ raise RequiredFailure unless azrow.constant.cl_approx_zero
237
+
238
+ if e = rows[av]
239
+ # Find another variable in this row and pivot, so that av
240
+ # becomes parametric. If there isn't another variable in the
241
+ # row then the tableau contains the equation av=0 -- just
242
+ # delete av's row.
243
+ if e.constant?
244
+ remove_row(av)
245
+ return nil
246
+ else
247
+ pivot(e.any_variable, av)
248
+ end
249
+ end
250
+
251
+ # av should be parametric at this point
252
+ remove_parametric_var av
253
+
254
+ # remove the temporary objective function
255
+ remove_row az
256
+ end
257
+
258
+ def choose_subject(expr)
259
+ # We are trying to add the constraint expr=0 to the tableaux.
260
+ # Try to choose a subject (a variable to become basic) from
261
+ # among the current variables in expr. If expr contains any
262
+ # unrestricted variables, then we must choose an unrestricted
263
+ # variable as the subject. Also, if the subject is new to the
264
+ # solver we won't have to do any substitutions, so we prefer new
265
+ # variables to ones that are currently noted as parametric. If
266
+ # expr contains only restricted variables, if there is a
267
+ # restricted variable with a negative coefficient that is new to
268
+ # the solver we can make that the subject. Otherwise we can't
269
+ # find a subject, so return nil. (In this last case we have to
270
+ # add an artificial variable and use that variable as the
271
+ # subject -- this is done outside this method though.)
272
+ #
273
+ # Note: in checking for variables that are new to the solver, we
274
+ # ignore whether a variable occurs in the objective function, since
275
+ # new slack variables are added to the objective function by
276
+ # 'makeExpression:', which is called before this method.
277
+ found_unrestricted = false
278
+ found_new_restricted = false
279
+ subject = nil
280
+ coeff = nil
281
+
282
+ expr.each_variable_and_coefficient do |v, c|
283
+ if found_unrestricted
284
+ # We have already found an unrestricted variable. The only
285
+ # time we will want to use v instead of the current choice
286
+ # 'subject' is if v is unrestricted and new to the solver
287
+ # and 'subject' isn't new. If this is the case just pick v
288
+ # immediately and return.
289
+ unless v.restricted?
290
+ return v unless columns.has_key? v
291
+ end
292
+ else
293
+ if v.restricted?
294
+ # v is restricted. If we have already found a suitable
295
+ # restricted variable just stick with that. Otherwise, if
296
+ # v is new to the solver and has a negative coefficient
297
+ # pick it. Regarding being new to the solver -- if the
298
+ # variable occurs only in the objective function we regard
299
+ # it as being new to the solver, since error variables are
300
+ # added to the objective function when we make the
301
+ # expression. We also never pick a dummy variable here.
302
+ if !found_new_restricted and !v.dummy? and c < 0.0
303
+ col = columns[v]
304
+ if col.nil? or (col.size == 1 and col.include? objective)
305
+ subject = v
306
+ found_new_restricted = true
307
+ end
308
+ end
309
+ else
310
+ # v is unrestricted. If v is also new to the solver just
311
+ # pick it now
312
+ return v unless columns.has_key? v
313
+ subject = v
314
+ found_unrestricted = true
315
+ end
316
+ end
317
+ end
318
+
319
+ # subject is nil. Make one last check -- if all of the
320
+ # variables in expr are dummy variables, then we can pick a
321
+ # dummy variable as the subject.
322
+ return subject unless subject.nil?
323
+ expr.each_variable_and_coefficient do |v, c|
324
+ return nil unless v.dummy?
325
+ # if v is new to the solver tentatively make it the subject
326
+ unless columns.has_key? v
327
+ subject = v
328
+ coeff = c
329
+ end
330
+ end
331
+
332
+ # If we get this far, all of the variables in the expression
333
+ # should be dummy variables. If the constant is nonzero we are
334
+ # trying to add an unsatisfiable required constraint. (Remember
335
+ # that dummy variables must take on a value of 0.) Otherwise,
336
+ # if the constant is zero, multiply by -1 if necessary to make
337
+ # the coefficient for the subject negative.
338
+ raise RequiredFailure unless expr.constant.cl_approx_zero
339
+ if coeff > 0
340
+ expr.each_variable_and_coefficient do |v, c|
341
+ expr.terms[v] = 0.0 - c
342
+ end
343
+ end
344
+
345
+ subject
346
+ end
347
+
348
+ def delta_edit_constant(delta, plus_error_var, minus_error_var)
349
+ if expr = rows[plus_error_var]
350
+ expr.increment_constant delta
351
+ # error variables are always restricted -- so the row is
352
+ # infeasible if the constant is negative
353
+ (infeasible_rows << plus_error_var) if expr.constant < 0.0
354
+ return nil
355
+ end
356
+
357
+ if expr = rows[minus_error_var]
358
+ expr.increment_constant -delta
359
+ (infeasible_rows << plus_error_var) if expr.constant < 0.0
360
+ return nil
361
+ end
362
+
363
+ # Neither minusErrorVar nor plusErrorVar is basic. So they must
364
+ # both be nonbasic, and will both occur in exactly the same
365
+ # expressions. Find all the expressions in which they occur by
366
+ # finding the column for the minusErrorVar (it doesn't matter
367
+ # whether we look for that one or for plusErrorVar). Fix the
368
+ # constants in these expressions.
369
+ columns[minus_error_var].each do |basic_var|
370
+ expr = rows[basic_var]
371
+ c = expr.coefficient_for(minus_error_var)
372
+ expr.increment_constant c * delta
373
+ if basic_var.restricted? and expr.constant < 0.0
374
+ infeasible_rows << basic_var
375
+ end
376
+ end
377
+ end
378
+
379
+ def dual_optimize
380
+ # We have set new values for the constants in the edit
381
+ # constraints. Re-optimize using the dual simplex algorithm.
382
+ entry_var = nil
383
+ zrow = rows[objective]
384
+ until infeasible_rows.empty?
385
+ exit_var = infeasible_rows.shift
386
+ if expr = rows[exit_var]
387
+ if expr.constant < 0.0
388
+ ratio = nil
389
+ expr.each_variable_and_coefficient do |v, c|
390
+ if c > 0.0 and v.pivotable?
391
+ zc = zrow.terms[v]
392
+ r = zc ? zc / c : SymbolicWeight::Zero
393
+ if ratio.nil? or r < ratio or (r == ratio and v.hash < entry_var.hash)
394
+ entry_var = v
395
+ ratio = r
396
+ end
397
+ end
398
+ end
399
+ raise InternalError if ratio.nil?
400
+ pivot entry_var, exit_var
401
+ end
402
+ end
403
+ end
404
+ end
405
+
406
+ def find_edit_error_index(evars)
407
+ evars.each do |v|
408
+ if index = edit_plus_error_vars.index(v)
409
+ return index
410
+ end
411
+ end
412
+ raise InternalError, "didn't find a variable"
413
+ end
414
+
415
+ def initialize
416
+ self.objective = ObjectiveVariable.new
417
+ self.rows = {objective => LinearExpression.new_with_symbolic_weight}
418
+ self.columns = {}
419
+ self.infeasible_rows = []
420
+ self.prev_edit_constants = []
421
+ self.stay_plus_error_vars = []
422
+ self.stay_minus_error_vars = []
423
+ self.edit_vars = []
424
+ self.edit_constraints = []
425
+ self.edit_plus_error_vars = []
426
+ self.edit_minus_error_vars = []
427
+ self.marker_vars = {}
428
+ self.error_vars = {}
429
+ self.auto_solve = true
430
+ end
431
+
432
+ def make_expression(cn)
433
+ # Make a new linear expression representing the constraint cn,
434
+ # replacing any basic variables with their defining expressions.
435
+ # Normalize if necessary so that the constant is non-negative.
436
+ # If the constraint is non-required give its error variables an
437
+ # appropriate weight in the objective function.
438
+ expr = LinearExpression.new
439
+ cnexpr = cn.expression
440
+ expr.constant = cnexpr.constant
441
+ cnexpr.each_variable_and_coefficient do |v, c|
442
+ e = rows[v]
443
+ if e.nil?
444
+ expr.add_variable(v, c)
445
+ else
446
+ expr.add_expression(e, c)
447
+ end
448
+ end
449
+
450
+ # add slack and error variables as needed
451
+ if cn.inequality?
452
+ # cn is an inequality, so add a slack variable. The original
453
+ # constraint is expr>=0, so that the resulting equality is
454
+ # expr-slackVar=0. If cn is also non-required add a negative
455
+ # error variable, giving expr-slackVar = -errorVar, in other
456
+ # words expr-slackVar+errorVar=0. Since both of these
457
+ # variables are newly created we can just add them to the
458
+ # expression (they can't be basic).
459
+ slackvar = SlackVariable.new
460
+ expr.terms[slackvar] = -1.0
461
+ marker_vars[cn] = slackvar
462
+ unless cn.required?
463
+ eminus = SlackVariable.new
464
+ expr.terms[eminus] = 1.0
465
+
466
+ zrow = rows[objective]
467
+ zrow.terms[eminus] = cn.strength.symbolic_weight * cn.weight
468
+ error_vars[cb] = [eminus]
469
+ note_added_variable(eminus, objective)
470
+ end
471
+ else
472
+ if cn.required?
473
+ # Add a dummy variable to the expression to serve as a
474
+ # marker for this constraint. The dummy variable is never
475
+ # allowed to enter the basis when pivoting.
476
+ dummyvar = DummyVariable.new
477
+ expr.terms[dummyvar] = 1.0
478
+ marker_vars[cn] = dummyvar
479
+ else
480
+ # cn is a non-required equality. Add a positive and a
481
+ # negative error variable, making the resulting constraint
482
+ # expr = eplus - eminus, in other words expr-eplus+eminus=0
483
+ eplus = SlackVariable.new
484
+ eminus = SlackVariable.new
485
+ expr.terms[eplus] = -1.0
486
+ expr.terms[eminus] = 1.0
487
+
488
+ # index the constraint under one of the error variables
489
+ marker_vars[cn] = eplus
490
+ zrow = rows[objective]
491
+ zrow.terms[eplus] = cn.strength.symbolic_weight * cn.weight
492
+ note_added_variable(eplus, objective)
493
+ zrow.terms[eminus] = cn.strength.symbolic_weight * cn.weight
494
+ error_vars[cn] = [eplus, eminus]
495
+ note_added_variable(eminus, objective)
496
+
497
+ if cn.stay_constraint?
498
+ stay_plus_error_vars << eplus
499
+ stay_minus_error_vars << eminus
500
+ end
501
+
502
+ if cn.edit_constraint?
503
+ edit_vars << cn.variable
504
+ edit_constraints << cn
505
+ edit_plus_error_vars << eplus
506
+ edit_minus_error_vars << eminus
507
+ prev_edit_constants << cnexpr.constant
508
+ end
509
+ end
510
+ end
511
+
512
+ # The constant in the expression should be non-negative. If
513
+ # necessary normalize the expression by multiplying by -1.
514
+ if expr.constant < 0
515
+ expr.constant = 0.0 - expr.constant
516
+ expr.each_variable_and_coefficient do |v, c|
517
+ expr.terms[v] = 0.0 - c
518
+ end
519
+ end
520
+ expr
521
+ end
522
+
523
+ def optimize(zvar)
524
+ # Minimize the value of the objective. (The tableau should
525
+ # already be feasible.)
526
+ zrow = rows[zvar]
527
+ exitvar = nil
528
+ while true do
529
+ # Find a variable in the objective function with a negative
530
+ # coefficient (ignoring dummy variables). If all coefficients
531
+ # are positive we're done. To implement Bland's anticycling
532
+ # rule, if there is more than one variable with a negative
533
+ # coefficient, pick the one with the smaller id (implemented
534
+ # as hash).
535
+ entryvar = nil
536
+ zrow.each_variable_and_coefficient do |v, c|
537
+ if v.pivotable? and c.definitely_negative and (entryvar.nil? or v.hash < entryvar.hash)
538
+ entryvar = v
539
+ end
540
+ end
541
+
542
+ # if all coefficients were positive (or if the objective
543
+ # function has no pivotable variables) we are at optimum
544
+ return nil if entryvar.nil?
545
+
546
+ # Choose which variable to move out of the basis. Only
547
+ # consider pivotable basic variables (that is, restricted,
548
+ # non-dummy variables).
549
+ minratio = nil
550
+ columns[entryvar].each do |v|
551
+ if v.pivotable?
552
+ expr = rows[v]
553
+ coeff = expr.coefficient_for(entryvar)
554
+
555
+ if coeff < 0.0
556
+ r = -(expr.constant / coeff)
557
+ # Decide whether to make v be the best choice for exit
558
+ # variable so far by comparing the ratios. In case of a
559
+ # tie, choose the variable with the smaller id (to
560
+ # implement Bland's anticycling rule).
561
+ if minratio.nil? or r < minratio or (r == minratio and v.hash < exitvar.hash)
562
+ minratio = r
563
+ exitvar = v
564
+ end
565
+ end
566
+ end
567
+ end
568
+
569
+ # If minRatio is still nil at this point, it means that the
570
+ # objective function is unbounded, i.e. it can become
571
+ # arbitrarily negative. This should never happen in this
572
+ # application.
573
+ raise InternalError if minratio.nil?
574
+ pivot entryvar, exitvar
575
+ end
576
+ end
577
+
578
+ def pivot(entryvar, exitvar)
579
+ # Do a pivot. Move entryVar into the basis (i.e. make it a
580
+ # basic variable), and move exitVar out of the basis (i.e. make
581
+ # it a parametric variable). expr is the expression for the
582
+ # exit variable (about to leave the basis) -- so that the old
583
+ # tableau includes the equation exitVar = expr
584
+ expr = remove_row(exitvar)
585
+
586
+ # Compute an expression for the entry variable. Since expr has
587
+ # been deleted from the tableau we can destructively modify it
588
+ # to build this expression.
589
+ expr.change_subject exitvar, entryvar
590
+ substitute_out(entryvar, expr)
591
+ add_row(entryvar, expr)
592
+ end
593
+
594
+ def remove_parametric_var(var)
595
+ set = columns.delete(var)
596
+ set.each do |v|
597
+ rows[v].terms.delete(var)
598
+ end
599
+ end
600
+
601
+ def remove_row(var)
602
+ expr = rows.delete(var)
603
+ expr.each_variable_and_coefficient do |v, c|
604
+ columns[v].delete var
605
+ end
606
+ infeasible_rows.delete(var)
607
+ expr
608
+ end
609
+
610
+ def reset_edit_constants
611
+ # Each of the non-required edits will be represented by an
612
+ # equation of the form
613
+ #
614
+ # v = c + eplus - eminus
615
+ #
616
+ # where v is the variable with the edit, c is the previous edit
617
+ # value, and eplus and eminus are slack variables that hold the
618
+ # error in satisfying the edit constraint. We are about to
619
+ # change something, and we want to fix the constants in the
620
+ # equations representing the edit constraints. If one of eplus
621
+ # and eminus is basic, the other must occur only in the
622
+ # expression for that basic error variable. (They can't both be
623
+ # basic.) Fix the constant in this expression. Otherwise they
624
+ # are both nonbasic. Find all of the expressions in which they
625
+ # occur, and fix the constants in those. See the UIST paper for
626
+ # details.
627
+
628
+ raise InternalError if new_edit_constants.size != edit_plus_error_vars.size
629
+ new_edit_constants.each_with_index do |ec, idx|
630
+ delta = ec - prev_edit_constants[idx]
631
+ prev_edit_constants[idx] = ec
632
+ delta_edit_constant(delta, edit_plus_error_vars[idx], edit_minus_error_vars[idx])
633
+ end
634
+ end
635
+
636
+ def reset_stay_constants
637
+ # Each of the non-required stays will be represented by an
638
+ # equation of the form
639
+ #
640
+ # v = c + eplus - eminus
641
+ #
642
+ # where v is the variable with the stay, c is the previous value
643
+ # of v, and eplus and eminus are slack variables that hold the
644
+ # error in satisfying the stay constraint. We are about to
645
+ # change something, and we want to fix the constants in the
646
+ # equations representing the stays. If both eplus and eminus
647
+ # are nonbasic they have value 0 in the current solution,
648
+ # meaning the previous stay was exactly satisfied. In this case
649
+ # nothing needs to be changed. Otherwise one of them is basic,
650
+ # and the other must occur only in the expression for that basic
651
+ # error variable. Reset the constant in this expression to 0.
652
+
653
+ stay_plus_error_vars.each_with_index do |ev, idx|
654
+ expr = rows[ev] || rows[stay_minus_error_vars[idx]]
655
+ expr.constant = 0.0 if expr
656
+ end
657
+ end
658
+
659
+ def set_external_variables
660
+ # Set each external basic variable to its value, and set each
661
+ # external parametric variable to 0. (It isn't clear that we
662
+ # will ever have external parametric variables -- every external
663
+ # variable should either have a stay on it, or have an equation
664
+ # that defines it in terms of other external variables that do
665
+ # have stays. For the moment I'll put this in though.)
666
+ # Variables that are internal to the solver don't actually store
667
+ # values -- their values are just implicit in the tableu -- so
668
+ # we don't need to set them.
669
+ rows.each_pair do |var, expr|
670
+ var.value = expr.constant if var.external?
671
+ end
672
+
673
+ columns.keys.each do |var|
674
+ var.value = 0.0 if var.external?
675
+ end
676
+ end
677
+
678
+ def substitute_out(old_var, expr)
679
+ col = columns.delete(old_var)
680
+ col.each do |v|
681
+ row = rows[v]
682
+ row.substitute_variable(old_var, expr, v, self)
683
+ if v.restricted? and row.constant < 0.0
684
+ infeasible_rows << v
685
+ end
686
+ end
687
+ end
688
+
689
+ def try_adding_directly(expr)
690
+ # If possible choose a subject for expr (a variable to become
691
+ # basic) from among the current variables in expr. If this
692
+ # isn't possible, add an artificial variable and use that
693
+ # variable as the subject.
694
+ subject = choose_subject(expr)
695
+ return false if subject.nil?
696
+ expr.new_subject subject
697
+ if columns.has_key? subject
698
+ substitute_out subject, expr
699
+ end
700
+ add_row subject, expr
701
+ true
702
+ end
703
+ end
704
+ end