-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbasis.lisp
259 lines (227 loc) · 8.81 KB
/
basis.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
(in-package :rationalsimplex)
;;;;; Basis data structure
;;;;;
;;;;; A basis object contains all data relevant to a particular basis:
;;;;; basic primal values, nonbasic primal values (lower or upper bound)
;;;;; nonbasic dual values (reduced costs), dual-steepest-edge weights, etc.
;;;;; Primal-infeasible values are stored in an appropriate data structure.
;;;; Structure required for primal infeasabilites vector
(splay-tree :val-type rational)
;;;;
(defstruct (basis
(:constructor %make-basis)
(:print-function print-basis))
(matrix nil :type basis-matrix) ; basis matrix factorization
(in-phase1 t :type boolean) ; T if basis values are for phase 1
;; contains column reference numbers of columns in basis
(header #() :type (simple-array fixnum 1))
;; dual-steepest-edge weights for good exiting variable selection
(dse-coef 1 :type rational)
(dse-weight-vis #() :type (simple-array integer 1))
;; nonbasic dual variable values
(reduced-costs #() :type (simple-array rational 1))
;; nonbasic primal variable values
(column-flags #() :type (simple-array symbol 1))
;; primal basic variable values
(primal-values #() :type (simple-array rational 1))
;; primal basic infeasible variables and their excess squared
(primal-infeas (error "basis constructor") :type splay-tree-fixnum-rational)
;; current objective value
(obj-value 0 :type rational))
(defun print-basis (b stream depth)
(declare (ignore depth))
(format stream "#BASIS{ Phase ~D, Objective = ~,5F}"
(if (basis-in-phase1 b) 1 2)
(coerce (basis-obj-value b) 'double-float)))
;;;; Sets nonbasic variables for initial basis in phase 1
(defun phase1-initial-nonbasic-flags (lp flags)
(dotimes (j (adjvector-column-fill-pointer (lp-columns lp)))
(let ((col (adjvector-column-ref (lp-columns lp) j)))
(setf (aref flags j)
(cond ((not (column-is-active col))
'inactive)
((column-is-slack col)
'basic)
((and (column-has-l col) (column-has-u col))
'boxed)
(t
(if (< (* (- (lp-obj-sense lp)) (column-c col)) 0)
'nonbasic-upper-bound
'nonbasic-lower-bound)))))))
;;;; Computes phase 1 objective from scratch
(defun phase1-initial-objective-value (lp flags)
(let ((n (adjvector-column-fill-pointer (lp-columns lp)))
(z 0))
(dotimes (j n z)
(let ((col (adjvector-column-ref (lp-columns lp) j))
(flag (aref flags j)))
(cond ((zerop (column-c col)))
((and (eq flag 'nonbasic-upper-bound)
(not (column-has-u col)))
(incf z (* (- (lp-obj-sense lp)) (column-c col))))
((and (eq flag 'nonbasic-lower-bound)
(not (column-has-l col)))
(decf z (* (- (lp-obj-sense lp)) (column-c col)))))))))
;;;; Gets amount of bound excess for primal infeasable basic variable
(defun get-delta (in-phase1 x col)
(if in-phase1
(cond ((and (column-has-l col)
(< x 0))
x)
((and (column-has-u col)
(< 0 x))
x)
(t 0))
(cond ((and (column-has-l col)
(< x (column-l col)))
(- x (column-l col)))
((and (column-has-u col)
(< (column-u col) x))
(- x (column-u col)))
(t 0))))
;;;; Generates a dual feasible basis using the slack variables
(defun make-phase1-initial-basis (lp basis-refactor-period)
(let* ((m (adjvector-fixnum-fill-pointer (lp-active-row-refs lp)))
(n (adjvector-column-fill-pointer (lp-columns lp)))
(header (make-array m :initial-element -1 :element-type 'fixnum))
(rcosts (make-array n :initial-element 0 :element-type 'rational))
(flags (make-array n :initial-element 'unknown :element-type 'symbol))
(weights (make-array m :initial-element 1 :element-type 'integer))
(values (make-array m :initial-element 0 :element-type 'rational))
(infeas (make-splay-tree-fixnum-rational)))
;; set flags to best bound
(phase1-initial-nonbasic-flags lp flags)
;; set phase1 reduced costs
(dotimes (j n)
(let ((flag (aref flags j)))
(when (or (eq flag 'nonbasic-lower-bound)
(eq flag 'nonbasic-upper-bound))
(setf (aref rcosts j) (lp-get-cost lp j)))))
;; build basis header from slack variables
;; and set basic variable values
(dotimes (i m)
(let* ((v 0)
(row-ref (adjvector-fixnum-ref (lp-active-row-refs lp) i))
(row (adjvector-row-ref (lp-rows lp) row-ref))
(slack-col-ref (row-slack-col-ref row))
(slack-col (adjvector-column-ref (lp-columns lp) slack-col-ref)))
(dotimes (k (adjvector-fixnum-fill-pointer (row-col-refs row)))
(let* ((col-ref (adjvector-fixnum-ref (row-col-refs row) k))
(flag (aref flags col-ref))
(col (adjvector-column-ref (lp-columns lp) col-ref))
(a (rational-in-column col (adjvector-fixnum-ref (row-col-indices row) k))))
(cond ((= col-ref slack-col-ref))
((and (eq flag 'nonbasic-lower-bound)
(not (column-has-l col)))
(decf v a))
((and (eq flag 'nonbasic-upper-bound)
(not (column-has-u col)))
(incf v a)))))
(setf v (- v))
(setf (aref values i) v
(aref header i) slack-col-ref)
(cond ((and (column-has-l slack-col) (< v 0))
(splay-tree-fixnum-rational-set infeas i (* v v)))
((and (column-has-u slack-col) (< 0 v))
(splay-tree-fixnum-rational-set infeas i (* v v))))))
;; return basis instance
(let ((matrix (make-basis-matrix :lp lp
:refactorization-period basis-refactor-period)))
(fill-basis-matrix matrix lp header -1 -1)
(basis-matrix-lu-factorization matrix)
(%make-basis
:matrix matrix
:header header
:obj-value (phase1-initial-objective-value lp flags)
:dse-weight-vis weights
:reduced-costs rcosts
:column-flags flags
:primal-values values
:primal-infeas infeas))))
;;;; Returns the value corresponding to a nonbasic variable bound
;;;; Varies for phases 1 and 2
(defun nonbasic-value (in-phase1 col flag)
(if in-phase1
(cond ((eq flag 'nonbasic-lower-bound)
(if (column-has-l col) 0 -1))
((eq flag 'nonbasic-upper-bound)
(if (column-has-u col) 0 1))
(t
(error "inappropriate flag for phase 1")))
(cond ((and (eq flag 'nonbasic-lower-bound)
(column-has-l col))
(column-l col))
((and (eq flag 'nonbasic-upper-bound)
(column-has-u col))
(column-u col))
(t
(error "inappropriate flag for phase 2")))))
;;;; Difference between upper and lower bound of variable
;;;; Varies for phases 1 and 2
(defun column-u-minus-l (in-phase1 col)
(if in-phase1
(let ((range 2))
(when (column-has-u col)
(decf range))
(when (column-has-l col)
(decf range))
range)
(- (column-u col) (column-l col))))
;;;; Difference between lower and upper bound of variable
;;;; Varies for phases 1 and 2
(defun column-l-minus-u (in-phase1 col)
(if in-phase1
(let ((range (- 2)))
(when (column-has-u col)
(incf range))
(when (column-has-l col)
(incf range))
range)
(- (column-l col) (column-u col))))
;;;; Updates primal infeasability vector depending on how
;;;; variable in i-th position of basis header varied in
;;;; its primal value x, and depending on phase 1 or 2
(defun basis-update-primal-infeasability (primal-infeas i x col inphase1)
(multiple-value-bind (sti there)
(splay-tree-fixnum-rational-find-key primal-infeas i)
(cond
((and inphase1
(or (and (column-has-l col) (< x 0))
(and (column-has-u col) (< 0 x))))
(if there
(setf (splay-tree-fixnum-rational-value primal-infeas sti) (* x x))
(splay-tree-fixnum-rational-set primal-infeas i (* x x))))
((and inphase1 there)
(splay-tree-fixnum-rational-remove primal-infeas i))
(inphase1)
((and (column-has-l col) (< x (column-l col)))
(let ((delta (- (column-l col) x)))
(mulf delta delta)
(if there
(setf (splay-tree-fixnum-rational-value primal-infeas sti) delta)
(splay-tree-fixnum-rational-set primal-infeas i delta))))
((and (column-has-u col) (< (column-u col) x))
(let ((delta (- x (column-u col))))
(mulf delta delta)
(if there
(setf (splay-tree-fixnum-rational-value primal-infeas sti) delta)
(splay-tree-fixnum-rational-set primal-infeas i delta))))
(there
(splay-tree-fixnum-rational-remove primal-infeas i)))))
;;;;; DEBUGGING
(defun check-infeas-vector (b lp)
(when *checks*
(let ((m (basis-matrix-size (basis-matrix b)))
(infeas (basis-primal-infeas b)))
(dotimes (i m)
(let* ((x (aref (basis-primal-values b) i))
(col-ref (aref (basis-header b) i))
(col (adjvector-column-ref (lp-columns lp) col-ref))
(delta (get-delta (basis-in-phase1 b) x col)))
(multiple-value-bind (sti there)
(splay-tree-fixnum-rational-find-key infeas i)
(if (zerop delta)
(assert (not there))
(assert (and there
(= (splay-tree-fixnum-rational-value infeas sti)
(* delta delta)))))))))))