1use crate::qmatrix::QMatrix;
10
11const TAU: f64 = 1e-12;
12const INF: f64 = f64::INFINITY;
13
14#[derive(Debug, Clone)]
16pub struct SolutionInfo {
17 pub obj: f64,
18 pub rho: f64,
19 pub upper_bound_p: f64,
20 pub upper_bound_n: f64,
21 pub r: f64,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27enum AlphaStatus {
28 LowerBound,
29 UpperBound,
30 Free,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum SolverVariant {
36 Standard,
37 Nu,
38}
39
40pub struct Solver<'a> {
42 l: usize,
43 active_size: usize,
44 variant: SolverVariant,
45
46 y: Vec<i8>,
47 g: Vec<f64>,
48 g_bar: Vec<f64>,
49 alpha: Vec<f64>,
50 alpha_status: Vec<AlphaStatus>,
51 p: Vec<f64>,
52 active_set: Vec<usize>,
53 unshrink: bool,
54
55 q: Box<dyn QMatrix + 'a>,
56 qd: Vec<f64>,
57 cp: f64,
58 cn: f64,
59 eps: f64,
60}
61
62impl<'a> Solver<'a> {
63 pub fn solve(
76 variant: SolverVariant,
77 l: usize,
78 q: Box<dyn QMatrix + 'a>,
79 p_: &[f64],
80 y_: &[i8],
81 alpha_: &mut [f64],
82 cp: f64,
83 cn: f64,
84 eps: f64,
85 shrinking: bool,
86 ) -> SolutionInfo {
87 let qd = q.get_qd().to_vec();
88 let p = p_.to_vec();
89 let y = y_.to_vec();
90 let alpha = alpha_.to_vec();
91
92 let mut solver = Solver {
93 l,
94 active_size: l,
95 variant,
96 y,
97 g: vec![0.0; l],
98 g_bar: vec![0.0; l],
99 alpha,
100 alpha_status: vec![AlphaStatus::LowerBound; l],
101 p,
102 active_set: (0..l).collect(),
103 unshrink: false,
104 q,
105 qd,
106 cp,
107 cn,
108 eps,
109 };
110
111 for i in 0..l {
113 solver.update_alpha_status(i);
114 }
115
116 for i in 0..l {
118 solver.g[i] = solver.p[i];
119 }
120
121 for i in 0..l {
122 if !solver.is_lower_bound(i) {
123 let alpha_i = solver.alpha[i];
124 let q_i = solver.q.get_q(i, l).to_vec();
125 for j in 0..l {
126 solver.g[j] += alpha_i * q_i[j] as f64;
127 }
128 if solver.is_upper_bound(i) {
129 let c_i = solver.get_c(i);
130 for j in 0..l {
131 solver.g_bar[j] += c_i * q_i[j] as f64;
132 }
133 }
134 }
135 }
136
137 let max_iter = 10_000_000usize.max(if l > i32::MAX as usize / 100 {
139 usize::MAX
140 } else {
141 100 * l
142 });
143 let mut counter = l.min(1000) + 1;
144 let mut iter = 0usize;
145
146 while iter < max_iter {
147 counter -= 1;
149 if counter == 0 {
150 counter = l.min(1000);
151 if shrinking {
152 solver.do_shrinking();
153 }
154 }
155
156 let (wi, wj) = match solver.select_working_set() {
157 Some(pair) => pair,
158 None => {
159 solver.reconstruct_gradient();
161 solver.active_size = l;
162 match solver.select_working_set() {
163 Some(pair) => {
164 counter = 1; pair
166 }
167 None => break, }
169 }
170 };
171
172 iter += 1;
173
174 solver.update_alpha_pair(wi, wj);
176 }
177
178 if iter >= max_iter {
179 if solver.active_size < l {
180 solver.reconstruct_gradient();
181 solver.active_size = l;
182 }
183 eprintln!("WARNING: reaching max number of iterations");
184 }
185
186 let (rho, r) = solver.calculate_rho();
188
189 let obj = {
191 let mut v = 0.0;
192 for i in 0..l {
193 v += solver.alpha[i] * (solver.g[i] + solver.p[i]);
194 }
195 v / 2.0
196 };
197
198 for i in 0..l {
200 alpha_[solver.active_set[i]] = solver.alpha[i];
201 }
202
203 let si = SolutionInfo {
204 obj,
205 rho,
206 upper_bound_p: cp,
207 upper_bound_n: cn,
208 r,
209 };
210
211 eprintln!(
212 "optimization finished, #iter = {}",
213 iter
214 );
215
216 si
217 }
218
219 #[inline]
222 fn get_c(&self, i: usize) -> f64 {
223 if self.y[i] > 0 { self.cp } else { self.cn }
224 }
225
226 #[inline]
227 fn update_alpha_status(&mut self, i: usize) {
228 self.alpha_status[i] = if self.alpha[i] >= self.get_c(i) {
229 AlphaStatus::UpperBound
230 } else if self.alpha[i] <= 0.0 {
231 AlphaStatus::LowerBound
232 } else {
233 AlphaStatus::Free
234 };
235 }
236
237 #[inline]
238 fn is_upper_bound(&self, i: usize) -> bool {
239 self.alpha_status[i] == AlphaStatus::UpperBound
240 }
241
242 #[inline]
243 fn is_lower_bound(&self, i: usize) -> bool {
244 self.alpha_status[i] == AlphaStatus::LowerBound
245 }
246
247 #[inline]
248 fn is_free(&self, i: usize) -> bool {
249 self.alpha_status[i] == AlphaStatus::Free
250 }
251
252 fn swap_index(&mut self, i: usize, j: usize) {
253 self.q.swap_index(i, j);
254 self.y.swap(i, j);
255 self.g.swap(i, j);
256 self.alpha_status.swap(i, j);
257 self.alpha.swap(i, j);
258 self.p.swap(i, j);
259 self.active_set.swap(i, j);
260 self.g_bar.swap(i, j);
261 self.qd.swap(i, j);
262 }
263
264 fn reconstruct_gradient(&mut self) {
265 if self.active_size == self.l {
266 return;
267 }
268
269 for j in self.active_size..self.l {
270 self.g[j] = self.g_bar[j] + self.p[j];
271 }
272
273 let mut nr_free = 0;
274 for j in 0..self.active_size {
275 if self.is_free(j) {
276 nr_free += 1;
277 }
278 }
279
280 if 2 * nr_free < self.active_size {
281 eprintln!("WARNING: using -h 0 may be faster");
282 }
283
284 let active_size = self.active_size;
285 let l = self.l;
286
287 if nr_free * l > 2 * active_size * (l - active_size) {
288 for i in active_size..l {
289 let q_i = self.q.get_q(i, active_size).to_vec();
290 for j in 0..active_size {
291 if self.is_free(j) {
292 self.g[i] += self.alpha[j] * q_i[j] as f64;
293 }
294 }
295 }
296 } else {
297 for i in 0..active_size {
298 if self.is_free(i) {
299 let q_i = self.q.get_q(i, l).to_vec();
300 let alpha_i = self.alpha[i];
301 for j in active_size..l {
302 self.g[j] += alpha_i * q_i[j] as f64;
303 }
304 }
305 }
306 }
307 }
308
309 fn select_working_set(&mut self) -> Option<(usize, usize)> {
313 match self.variant {
314 SolverVariant::Standard => self.select_working_set_standard(),
315 SolverVariant::Nu => self.select_working_set_nu(),
316 }
317 }
318
319 fn select_working_set_standard(&mut self) -> Option<(usize, usize)> {
320 let mut gmax = -INF;
321 let mut gmax2 = -INF;
322 let mut gmax_idx: Option<usize> = None;
323 let mut gmin_idx: Option<usize> = None;
324 let mut obj_diff_min = INF;
325
326 for t in 0..self.active_size {
328 if self.y[t] == 1 {
329 if !self.is_upper_bound(t) && -self.g[t] >= gmax {
330 gmax = -self.g[t];
331 gmax_idx = Some(t);
332 }
333 } else {
334 if !self.is_lower_bound(t) && self.g[t] >= gmax {
335 gmax = self.g[t];
336 gmax_idx = Some(t);
337 }
338 }
339 }
340
341 let i = gmax_idx?;
342 let q_i = self.q.get_q(i, self.active_size).to_vec();
343
344 for j in 0..self.active_size {
346 if self.y[j] == 1 {
347 if !self.is_lower_bound(j) {
348 let grad_diff = gmax + self.g[j];
349 if self.g[j] >= gmax2 {
350 gmax2 = self.g[j];
351 }
352 if grad_diff > 0.0 {
353 let quad_coef = self.qd[i] + self.qd[j]
354 - 2.0 * (self.y[i] as f64) * q_i[j] as f64;
355 let obj_diff = if quad_coef > 0.0 {
356 -(grad_diff * grad_diff) / quad_coef
357 } else {
358 -(grad_diff * grad_diff) / TAU
359 };
360 if obj_diff <= obj_diff_min {
361 gmin_idx = Some(j);
362 obj_diff_min = obj_diff;
363 }
364 }
365 }
366 } else {
367 if !self.is_upper_bound(j) {
368 let grad_diff = gmax - self.g[j];
369 if -self.g[j] >= gmax2 {
370 gmax2 = -self.g[j];
371 }
372 if grad_diff > 0.0 {
373 let quad_coef = self.qd[i] + self.qd[j]
374 + 2.0 * (self.y[i] as f64) * q_i[j] as f64;
375 let obj_diff = if quad_coef > 0.0 {
376 -(grad_diff * grad_diff) / quad_coef
377 } else {
378 -(grad_diff * grad_diff) / TAU
379 };
380 if obj_diff <= obj_diff_min {
381 gmin_idx = Some(j);
382 obj_diff_min = obj_diff;
383 }
384 }
385 }
386 }
387 }
388
389 if gmax + gmax2 < self.eps || gmin_idx.is_none() {
390 return None;
391 }
392
393 Some((i, gmin_idx.unwrap()))
394 }
395
396 fn select_working_set_nu(&mut self) -> Option<(usize, usize)> {
397 let mut gmaxp = -INF;
398 let mut gmaxp2 = -INF;
399 let mut gmaxp_idx: Option<usize> = None;
400 let mut gmaxn = -INF;
401 let mut gmaxn2 = -INF;
402 let mut gmaxn_idx: Option<usize> = None;
403 let mut gmin_idx: Option<usize> = None;
404 let mut obj_diff_min = INF;
405
406 for t in 0..self.active_size {
407 if self.y[t] == 1 {
408 if !self.is_upper_bound(t) && -self.g[t] >= gmaxp {
409 gmaxp = -self.g[t];
410 gmaxp_idx = Some(t);
411 }
412 } else {
413 if !self.is_lower_bound(t) && self.g[t] >= gmaxn {
414 gmaxn = self.g[t];
415 gmaxn_idx = Some(t);
416 }
417 }
418 }
419
420 let ip = gmaxp_idx;
421 let in_ = gmaxn_idx;
422
423 let q_ip = if let Some(ip) = ip {
424 Some(self.q.get_q(ip, self.active_size).to_vec())
425 } else {
426 None
427 };
428 let q_in = if let Some(in_) = in_ {
429 Some(self.q.get_q(in_, self.active_size).to_vec())
430 } else {
431 None
432 };
433
434 for j in 0..self.active_size {
435 if self.y[j] == 1 {
436 if !self.is_lower_bound(j) {
437 let grad_diff = gmaxp + self.g[j];
438 if self.g[j] >= gmaxp2 {
439 gmaxp2 = self.g[j];
440 }
441 if grad_diff > 0.0 {
442 if let (Some(ip), Some(ref q_ip)) = (ip, &q_ip) {
443 let quad_coef = self.qd[ip] + self.qd[j] - 2.0 * q_ip[j] as f64;
444 let obj_diff = if quad_coef > 0.0 {
445 -(grad_diff * grad_diff) / quad_coef
446 } else {
447 -(grad_diff * grad_diff) / TAU
448 };
449 if obj_diff <= obj_diff_min {
450 gmin_idx = Some(j);
451 obj_diff_min = obj_diff;
452 }
453 }
454 }
455 }
456 } else {
457 if !self.is_upper_bound(j) {
458 let grad_diff = gmaxn - self.g[j];
459 if -self.g[j] >= gmaxn2 {
460 gmaxn2 = -self.g[j];
461 }
462 if grad_diff > 0.0 {
463 if let (Some(in_), Some(ref q_in)) = (in_, &q_in) {
464 let quad_coef = self.qd[in_] + self.qd[j] - 2.0 * q_in[j] as f64;
465 let obj_diff = if quad_coef > 0.0 {
466 -(grad_diff * grad_diff) / quad_coef
467 } else {
468 -(grad_diff * grad_diff) / TAU
469 };
470 if obj_diff <= obj_diff_min {
471 gmin_idx = Some(j);
472 obj_diff_min = obj_diff;
473 }
474 }
475 }
476 }
477 }
478 }
479
480 if f64::max(gmaxp + gmaxp2, gmaxn + gmaxn2) < self.eps || gmin_idx.is_none() {
481 return None;
482 }
483
484 let out_j = gmin_idx.unwrap();
485 let out_i = if self.y[out_j] == 1 {
486 gmaxp_idx?
487 } else {
488 gmaxn_idx?
489 };
490
491 Some((out_i, out_j))
492 }
493
494 fn update_alpha_pair(&mut self, i: usize, j: usize) {
497 let active_size = self.active_size;
498 let q_i = self.q.get_q(i, active_size).to_vec();
499 let q_j = self.q.get_q(j, active_size).to_vec();
500
501 let c_i = self.get_c(i);
502 let c_j = self.get_c(j);
503
504 let old_alpha_i = self.alpha[i];
505 let old_alpha_j = self.alpha[j];
506
507 if self.y[i] != self.y[j] {
508 let mut quad_coef = self.qd[i] + self.qd[j] + 2.0 * q_i[j] as f64;
509 if quad_coef <= 0.0 {
510 quad_coef = TAU;
511 }
512 let delta = (-self.g[i] - self.g[j]) / quad_coef;
513 let diff = self.alpha[i] - self.alpha[j];
514 self.alpha[i] += delta;
515 self.alpha[j] += delta;
516
517 if diff > 0.0 {
518 if self.alpha[j] < 0.0 {
519 self.alpha[j] = 0.0;
520 self.alpha[i] = diff;
521 }
522 } else {
523 if self.alpha[i] < 0.0 {
524 self.alpha[i] = 0.0;
525 self.alpha[j] = -diff;
526 }
527 }
528 if diff > c_i - c_j {
529 if self.alpha[i] > c_i {
530 self.alpha[i] = c_i;
531 self.alpha[j] = c_i - diff;
532 }
533 } else {
534 if self.alpha[j] > c_j {
535 self.alpha[j] = c_j;
536 self.alpha[i] = c_j + diff;
537 }
538 }
539 } else {
540 let mut quad_coef = self.qd[i] + self.qd[j] - 2.0 * q_i[j] as f64;
541 if quad_coef <= 0.0 {
542 quad_coef = TAU;
543 }
544 let delta = (self.g[i] - self.g[j]) / quad_coef;
545 let sum = self.alpha[i] + self.alpha[j];
546 self.alpha[i] -= delta;
547 self.alpha[j] += delta;
548
549 if sum > c_i {
550 if self.alpha[i] > c_i {
551 self.alpha[i] = c_i;
552 self.alpha[j] = sum - c_i;
553 }
554 } else {
555 if self.alpha[j] < 0.0 {
556 self.alpha[j] = 0.0;
557 self.alpha[i] = sum;
558 }
559 }
560 if sum > c_j {
561 if self.alpha[j] > c_j {
562 self.alpha[j] = c_j;
563 self.alpha[i] = sum - c_j;
564 }
565 } else {
566 if self.alpha[i] < 0.0 {
567 self.alpha[i] = 0.0;
568 self.alpha[j] = sum;
569 }
570 }
571 }
572
573 let delta_alpha_i = self.alpha[i] - old_alpha_i;
575 let delta_alpha_j = self.alpha[j] - old_alpha_j;
576
577 for k in 0..active_size {
578 self.g[k] += q_i[k] as f64 * delta_alpha_i + q_j[k] as f64 * delta_alpha_j;
579 }
580
581 let ui = self.is_upper_bound(i);
583 let uj = self.is_upper_bound(j);
584 self.update_alpha_status(i);
585 self.update_alpha_status(j);
586
587 let l = self.l;
588
589 if ui != self.is_upper_bound(i) {
590 let q_i_full = self.q.get_q(i, l).to_vec();
591 if ui {
592 for k in 0..l {
593 self.g_bar[k] -= c_i * q_i_full[k] as f64;
594 }
595 } else {
596 for k in 0..l {
597 self.g_bar[k] += c_i * q_i_full[k] as f64;
598 }
599 }
600 }
601
602 if uj != self.is_upper_bound(j) {
603 let q_j_full = self.q.get_q(j, l).to_vec();
604 if uj {
605 for k in 0..l {
606 self.g_bar[k] -= c_j * q_j_full[k] as f64;
607 }
608 } else {
609 for k in 0..l {
610 self.g_bar[k] += c_j * q_j_full[k] as f64;
611 }
612 }
613 }
614 }
615
616 fn do_shrinking(&mut self) {
619 match self.variant {
620 SolverVariant::Standard => self.do_shrinking_standard(),
621 SolverVariant::Nu => self.do_shrinking_nu(),
622 }
623 }
624
625 fn be_shrunk_standard(&self, i: usize, gmax1: f64, gmax2: f64) -> bool {
626 if self.is_upper_bound(i) {
627 if self.y[i] == 1 {
628 -self.g[i] > gmax1
629 } else {
630 -self.g[i] > gmax2
631 }
632 } else if self.is_lower_bound(i) {
633 if self.y[i] == 1 {
634 self.g[i] > gmax2
635 } else {
636 self.g[i] > gmax1
637 }
638 } else {
639 false
640 }
641 }
642
643 fn do_shrinking_standard(&mut self) {
644 let mut gmax1 = -INF;
645 let mut gmax2 = -INF;
646
647 for i in 0..self.active_size {
648 if self.y[i] == 1 {
649 if !self.is_upper_bound(i) && -self.g[i] >= gmax1 {
650 gmax1 = -self.g[i];
651 }
652 if !self.is_lower_bound(i) && self.g[i] >= gmax2 {
653 gmax2 = self.g[i];
654 }
655 } else {
656 if !self.is_upper_bound(i) && -self.g[i] >= gmax2 {
657 gmax2 = -self.g[i];
658 }
659 if !self.is_lower_bound(i) && self.g[i] >= gmax1 {
660 gmax1 = self.g[i];
661 }
662 }
663 }
664
665 if !self.unshrink && gmax1 + gmax2 <= self.eps * 10.0 {
666 self.unshrink = true;
667 self.reconstruct_gradient();
668 self.active_size = self.l;
669 }
670
671 let mut i = 0;
672 while i < self.active_size {
673 if self.be_shrunk_standard(i, gmax1, gmax2) {
674 self.active_size -= 1;
675 while self.active_size > i {
676 if !self.be_shrunk_standard(self.active_size, gmax1, gmax2) {
677 self.swap_index(i, self.active_size);
678 break;
679 }
680 self.active_size -= 1;
681 }
682 }
683 i += 1;
684 }
685 }
686
687 fn be_shrunk_nu(&self, i: usize, gmax1: f64, gmax2: f64, gmax3: f64, gmax4: f64) -> bool {
688 if self.is_upper_bound(i) {
689 if self.y[i] == 1 {
690 -self.g[i] > gmax1
691 } else {
692 -self.g[i] > gmax4
693 }
694 } else if self.is_lower_bound(i) {
695 if self.y[i] == 1 {
696 self.g[i] > gmax2
697 } else {
698 self.g[i] > gmax3
699 }
700 } else {
701 false
702 }
703 }
704
705 fn do_shrinking_nu(&mut self) {
706 let mut gmax1 = -INF;
707 let mut gmax2 = -INF;
708 let mut gmax3 = -INF;
709 let mut gmax4 = -INF;
710
711 for i in 0..self.active_size {
712 if !self.is_upper_bound(i) {
713 if self.y[i] == 1 {
714 if -self.g[i] > gmax1 { gmax1 = -self.g[i]; }
715 } else {
716 if -self.g[i] > gmax4 { gmax4 = -self.g[i]; }
717 }
718 }
719 if !self.is_lower_bound(i) {
720 if self.y[i] == 1 {
721 if self.g[i] > gmax2 { gmax2 = self.g[i]; }
722 } else {
723 if self.g[i] > gmax3 { gmax3 = self.g[i]; }
724 }
725 }
726 }
727
728 if !self.unshrink && f64::max(gmax1 + gmax2, gmax3 + gmax4) <= self.eps * 10.0 {
729 self.unshrink = true;
730 self.reconstruct_gradient();
731 self.active_size = self.l;
732 }
733
734 let mut i = 0;
735 while i < self.active_size {
736 if self.be_shrunk_nu(i, gmax1, gmax2, gmax3, gmax4) {
737 self.active_size -= 1;
738 while self.active_size > i {
739 if !self.be_shrunk_nu(self.active_size, gmax1, gmax2, gmax3, gmax4) {
740 self.swap_index(i, self.active_size);
741 break;
742 }
743 self.active_size -= 1;
744 }
745 }
746 i += 1;
747 }
748 }
749
750 fn calculate_rho(&self) -> (f64, f64) {
753 match self.variant {
754 SolverVariant::Standard => (self.calculate_rho_standard(), 0.0),
755 SolverVariant::Nu => self.calculate_rho_nu(),
756 }
757 }
758
759 fn calculate_rho_standard(&self) -> f64 {
760 let mut nr_free = 0;
761 let mut ub = INF;
762 let mut lb = -INF;
763 let mut sum_free = 0.0;
764
765 for i in 0..self.active_size {
766 let yg = self.y[i] as f64 * self.g[i];
767
768 if self.is_upper_bound(i) {
769 if self.y[i] == -1 {
770 ub = ub.min(yg);
771 } else {
772 lb = lb.max(yg);
773 }
774 } else if self.is_lower_bound(i) {
775 if self.y[i] == 1 {
776 ub = ub.min(yg);
777 } else {
778 lb = lb.max(yg);
779 }
780 } else {
781 nr_free += 1;
782 sum_free += yg;
783 }
784 }
785
786 if nr_free > 0 {
787 sum_free / nr_free as f64
788 } else {
789 (ub + lb) / 2.0
790 }
791 }
792
793 fn calculate_rho_nu(&self) -> (f64, f64) {
794 let mut nr_free1 = 0;
795 let mut nr_free2 = 0;
796 let mut ub1 = INF;
797 let mut ub2 = INF;
798 let mut lb1 = -INF;
799 let mut lb2 = -INF;
800 let mut sum_free1 = 0.0;
801 let mut sum_free2 = 0.0;
802
803 for i in 0..self.active_size {
804 if self.y[i] == 1 {
805 if self.is_upper_bound(i) {
806 lb1 = lb1.max(self.g[i]);
807 } else if self.is_lower_bound(i) {
808 ub1 = ub1.min(self.g[i]);
809 } else {
810 nr_free1 += 1;
811 sum_free1 += self.g[i];
812 }
813 } else {
814 if self.is_upper_bound(i) {
815 lb2 = lb2.max(self.g[i]);
816 } else if self.is_lower_bound(i) {
817 ub2 = ub2.min(self.g[i]);
818 } else {
819 nr_free2 += 1;
820 sum_free2 += self.g[i];
821 }
822 }
823 }
824
825 let r1 = if nr_free1 > 0 {
826 sum_free1 / nr_free1 as f64
827 } else {
828 (ub1 + lb1) / 2.0
829 };
830
831 let r2 = if nr_free2 > 0 {
832 sum_free2 / nr_free2 as f64
833 } else {
834 (ub2 + lb2) / 2.0
835 };
836
837 let rho = (r1 - r2) / 2.0;
838 let r = (r1 + r2) / 2.0;
839 (rho, r)
840 }
841}