RsBundle  Diff

Differences From Artifact [8002945ba0]:

  • File src/solver.rs — part of check-in [16ede320bb] at 2017-11-19 20:30:27 on branch trunk — Add possible error handling to master problem methods (user: fifr size: 34282)

To Artifact [65aaf151b7]:

  • File src/solver.rs — part of check-in [b0a2986e97] at 2017-11-20 07:55:39 on branch trunk — Add SolverError variants for master and oracle errors. (user: fifr size: 35165)

24
25
26
27
28
29
30
31
32


33









34
35
36
37
38
39
40
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







-
-
+
+

+
+
+
+
+
+
+
+
+







use std::mem::swap;
use std::f64::{INFINITY, NEG_INFINITY};
use std::time::Instant;
use std::result::Result;

use failure::Error;

    /// A solver error.
    #[derive(Debug, Fail)]
/// A solver error.
#[derive(Debug, Fail)]
pub enum SolverError {
    /// An error occured during oracle evaluation.
    #[fail(display = "Oracle evaluation failed: {}", _0)]
    Evaluation(Error),
    /// An error occured during oracle update.
    #[fail(display = "Oracle update failed: {}", _0)]
    Update(Error),
    /// An error has been raised by the master problem.
    #[fail(display = "Master problem failed: {}", _0)]
    Master(Error),
    /// The oracle did not return a minorant.
    #[fail(display = "The oracle did not return a minorant")]
    NoMinorant,
    /// The dimension of some data is wrong.
    #[fail(display = "Dimension of lower bounds does not match number of variables")]
    Dimension,
    /// Some parameter has an invalid value.
422
423
424
425
426
427
428
429

430
431
432
433
434
435
436
431
432
433
434
435
436
437

438
439
440
441
442
443
444
445







-
+







     *
     * Note that the solver owns the problem, so you cannot use the
     * same problem description elsewhere as long as it is assigned to
     * the solver. However, it is possible to get a reference to the
     * internally stored problem using `Solver::problem()`.
     */
    pub fn new_params(problem: P, params: SolverParams)
                      -> Result<Solver<P, Pr, E>, Error>
                      -> Result<Solver<P, Pr, E>, SolverError>
    {
        Ok(Solver {
            problem: problem,
            params: params,
            terminator: Box::new(StandardTerminator { termination_precision: 1e-3 }),
            weighter: Box::new(HKWeighter::new()),
            bounds: vec![],
448
449
450
451
452
453
454
455

456
457
458
459
460
461
462

463
464
465
466
467
468
469
457
458
459
460
461
462
463

464
465
466
467
468
469
470

471
472
473
474
475
476
477
478







-
+






-
+







            nxt_mods: dvec![],
            new_cutval: 0.0,
            sgnorm: 0.0,
            expected_progress: 0.0,
            cnt_descent: 0,
            cnt_null: 0,
            start_time: Instant::now(),
            master: Box::new(BoxedMasterProblem::<MinimalMaster>::new()?),
            master: Box::new(BoxedMasterProblem::<MinimalMaster>::new().map_err(SolverError::Master)?),
            minorants: vec![],
            iterinfos: vec![],
        })
    }

    /// A new solver with default parameter.
    pub fn new(problem: P) -> Result<Solver<P, Pr, E>, Error> {
    pub fn new(problem: P) -> Result<Solver<P, Pr, E>, SolverError> {
        Solver::new_params(problem, SolverParams::default())
    }

    /**
     * Set the first order problem description associated with this
     * solver.
     *
520
521
522
523
524
525
526
527

528
529
530
531
532
533

534
535
536
537
538
539
540
541
542
543
544
545
546

547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566

567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586

587
588
589
590
591
592
593
594

595
596
597
598
599
600
601
602
603
604
605
606
607
608

609
610
611

612
613
614
615
616
617
618
619
620

621
622
623
624

625
626
627
628
629
630
631
632
633
634
635
636
637
638
639

640
641
642
643
644
645
646
529
530
531
532
533
534
535

536
537
538
539
540
541

542
543
544
545
546
547
548
549
550
551
552
553
554

555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574

575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594

595
596
597
598
599
600
601
602

603
604
605
606
607
608
609
610
611
612
613
614
615
616

617
618
619

620
621
622
623
624
625
626
627
628

629
630
631
632

633
634
635
636
637
638
639
640
641
642
643
644
645
646
647

648
649
650
651
652
653
654
655







-
+





-
+












-
+



















-
+



















-
+







-
+













-
+


-
+








-
+



-
+














-
+








        self.start_time = Instant::now();

        Ok(())
    }

    /// Solve the problem.
    pub fn solve(&mut self) -> Result<(), Error> {
    pub fn solve(&mut self) -> Result<(), SolverError> {
        const LIMIT: usize = 10_000;

        if self.solve_iter(LIMIT)? {
            Ok(())
        } else {
            Err(SolverError::IterationLimit { limit: LIMIT }.into())
            Err(SolverError::IterationLimit { limit: LIMIT })
        }
    }

    /// Solve the problem but stop after `niter` iterations.
    ///
    /// The function returns `Ok(true)` if the termination criterion
    /// has been satisfied. Otherwise it returns `Ok(false)` or an
    /// error code.
    ///
    /// If this function is called again, the solution process is
    /// continued from the previous point. Because of this one must
    /// call `init()` before the first call to this function.
    pub fn solve_iter(&mut self, niter: usize) -> Result<bool, Error> {
    pub fn solve_iter(&mut self, niter: usize) -> Result<bool, SolverError> {
        for _ in 0..niter {
            let mut term = try!(self.step());
            let changed = try!(self.update_problem(term));
            // do not stop if the problem has been changed
            if changed && term == Step::Term {
                term = Step::Null
            }
            self.show_info(term);
            if term == Step::Term {
                return Ok(true)
            }
        }
        Ok(false)
    }

    /// Called to update the problem.
    ///
    /// Calling this function typically triggers the problem to
    /// separate new constraints depending on the current solution.
    fn update_problem(&mut self, term: Step) -> Result<bool, Error> {
    fn update_problem(&mut self, term: Step) -> Result<bool, SolverError> {
        let updates = {
            let state = UpdateState {
                minorants: &self.minorants,
                step: term,
                iteration_info: &self.iterinfos,
                // this is a dirty trick: when updating the center, we
                // simply swapped the `cur_*` fields with the `nxt_*`
                // fields
                cur_y: if term == Step::Descent {
                    &self.nxt_y
                } else {
                    &self.cur_y
                },
                nxt_y: if term == Step::Descent {
                    &self.cur_y
                } else {
                    &self.nxt_y
                },
            };
            self.problem.update(&state)?
            self.problem.update(&state).map_err(SolverError::Update)?
        };

        let mut newvars = Vec::with_capacity(updates.len());
        for u in updates {
            match u {
                Update::AddVariable { lower, upper } => {
                    if lower > upper {
                        return Err(SolverError::InvalidBounds { lower, upper }.into());
                        return Err(SolverError::InvalidBounds { lower, upper });
                    }
                    let value = if lower > 0.0 {
                        lower
                    } else if upper < 0.0 {
                        upper
                    } else {
                        0.0
                    };
                    self.bounds.push((lower, upper));
                    newvars.push((None, lower - value, upper - value, value));
                }
                Update::AddVariableValue { lower, upper, value } => {
                    if lower > upper {
                        return Err(SolverError::InvalidBounds { lower, upper }.into());
                        return Err(SolverError::InvalidBounds { lower, upper });
                    }
                    if value < lower || value > upper {
                        return Err(SolverError::ViolatedBounds { lower, upper, value }.into());
                        return Err(SolverError::ViolatedBounds { lower, upper, value });
                    }
                    self.bounds.push((lower, upper));
                    newvars.push((None, lower - value, upper - value, value));
                }
                Update::MoveVariable { index, value } => {
                    if index >= self.bounds.len() {
                        return Err(SolverError::InvalidVariable {
                            index, nvars: self.bounds.len()
                        }.into());
                        });
                    }
                    let (lower, upper) = self.bounds[index];
                    if value < lower || value > upper {
                        return Err(SolverError::ViolatedBounds { lower, upper, value }.into());
                        return Err(SolverError::ViolatedBounds { lower, upper, value });
                    }
                    newvars.push((Some(index), lower - value, upper - value, value));
                }
            }
        }

        if !newvars.is_empty() {
            let mut problem = &mut self.problem;
            let minorants = &self.minorants;
            self.master.add_vars(&newvars.iter().map(|v| (v.0, v.1, v.2)).collect::<Vec<_>>(),
                                 &mut move |fidx, minidx, vars| {
                                     problem.extend_subgradient(minorants[fidx][minidx].primal.as_ref().unwrap(), vars)
                                         .map(DVector)
                                         .unwrap()
                                 })?;
                                 }).map_err(SolverError::Master)?;
            // modify moved variables
            for (index, val) in newvars.iter().filter_map(|v| v.0.map(|i| (i, v.3))) {
                self.cur_y[index] = val;
                self.nxt_y[index] = val;
                self.nxt_d[index] = 0.0;
            }
            // add new variables
703
704
705
706
707
708
709
710

711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726

727
728
729
730
731
732



733
734
735
736
737
738
739
740
741

742
743
744
745
746
747
748
749
750

751
752
753
754
755

756
757
758
759
760
761
762
763
764
765
766
767
768
769
770


771
772
773
774
775
776

777
778
779
780
781
782
783
784
785
786


787
788
789
790
791
792
793
712
713
714
715
716
717
718

719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734

735
736
737
738



739
740
741
742
743
744
745
746
747
748
749

750
751
752
753
754
755
756
757
758

759
760
761
762
763

764
765
766
767
768
769
770
771
772
773
774
775
776
777


778
779
780
781
782
783
784

785
786
787
788
789
790
791
792
793


794
795
796
797
798
799
800
801
802







-
+















-
+



-
-
-
+
+
+








-
+








-
+




-
+













-
-
+
+





-
+








-
-
+
+







    /**
     * Initializes the master problem.
     *
     * The oracle is evaluated once at the initial center and the
     * master problem is initialized with the returned subgradient
     * information.
     */
    fn init_master(&mut self) -> Result<(), Error> {
    fn init_master(&mut self) -> Result<(), SolverError> {
        let m = self.problem.num_subproblems();

        self.master = if m == 1 && self.params.max_bundle_size == 2 {
            debug!("Use minimal master problem");
            Box::new(BoxedMasterProblem::<MinimalMaster>::new().unwrap())
        } else {
            debug!("Use CPLEX master problem");
            Box::new(BoxedMasterProblem::<CplexMaster>::new().unwrap())
        };

        let lb = self.problem.lower_bounds().map(DVector);
        let ub = self.problem.upper_bounds().map(DVector);

        if let Some(ref x) = lb {
            if x.len() != self.problem.num_variables() {
                return Err(SolverError::Dimension.into());
                return Err(SolverError::Dimension);
            }
        }

        self.master.set_num_subproblems(m)?;
        self.master.set_vars(self.problem.num_variables(), lb, ub)?;
        self.master.set_max_updates(self.params.max_updates)?;
        self.master.set_num_subproblems(m).map_err(SolverError::Master)?;
        self.master.set_vars(self.problem.num_variables(), lb, ub).map_err(SolverError::Master)?;
        self.master.set_max_updates(self.params.max_updates).map_err(SolverError::Master)?;

        self.minorants = Vec::with_capacity(m);
        for _ in 0..m {
            self.minorants.push(vec![]);
        }

        self.cur_val = 0.0;
        for i in 0..m {
            let result = self.problem.evaluate(i, &self.cur_y, INFINITY, 0.0)?;
            let result = self.problem.evaluate(i, &self.cur_y, INFINITY, 0.0).map_err(SolverError::Master)?;
            self.cur_vals[i] = result.objective();
            self.cur_val += self.cur_vals[i];

            let mut minorants = result.into_iter();
            if let Some((minorant, primal)) = minorants.next() {
                self.cur_mods[i] = minorant.constant;
                self.cur_mod += self.cur_mods[i];
                self.minorants[i].push(MinorantInfo {
                    index: try!(self.master.add_minorant(i, minorant)),
                    index: self.master.add_minorant(i, minorant).map_err(SolverError::Master)?,
                    multiplier: 0.0,
                    primal: Some(primal),
                });
            } else {
                return Err(SolverError::NoMinorant.into());
                return Err(SolverError::NoMinorant);
            }
        }

        self.cur_valid = true;

        // Solve the master problem once to compute the initial
        // subgradient.
        //
        // We could compute that subgradient directly by
        // adding up the initial minorants, but this would not include
        // the eta terms. However, this is a heuristic anyway because
        // we assume an initial weight of 1.0, which, in general, will
        // *not* be the initial weight for the first iteration.
        self.master.set_weight(1.0)?;
        try!(self.master.solve(self.cur_val));
        self.master.set_weight(1.0).map_err(SolverError::Master)?;
        self.master.solve(self.cur_val).map_err(SolverError::Master)?;
        self.sgnorm = self.master.get_dualoptnorm2().sqrt();

        // Compute the real initial weight.
        let state = current_state!(self, Step::Term);
        let new_weight = self.weighter.weight(&state, &self.params);
        self.master.set_weight(new_weight)?;
        self.master.set_weight(new_weight).map_err(SolverError::Master)?;

        debug!("Init master completed");

        Ok(())
    }


    /// Solve the model (i.e. master problem) to compute the next candidate.
    fn solve_model(&mut self) -> Result<(), Error> {
        try!(self.master.solve(self.cur_val));
    fn solve_model(&mut self) -> Result<(), SolverError> {
        self.master.solve(self.cur_val).map_err(SolverError::Master)?;
        self.nxt_d = self.master.get_primopt();
        self.nxt_y.add(&self.cur_y, &self.nxt_d);
        self.nxt_mod = self.master.get_primoptval();
        self.sgnorm = self.master.get_dualoptnorm2().sqrt();
        self.expected_progress = self.cur_val - self.nxt_mod;

        // update multiplier from master solution
802
803
804
805
806
807
808
809

810
811
812
813
814
815
816
817
818
819
820

821
822
823
824
825
826
827
828
829
830
831
832
833
834
835

836
837

838
839
840
841
842
843
844
845
846
847
848
849
850
851
852

853
854

855
856
857
858
859
860
861
862

863
864
865
866
867
868
869
811
812
813
814
815
816
817

818
819
820
821
822
823
824
825
826
827
828

829
830
831
832
833
834
835
836
837
838
839
840
841
842
843

844
845

846
847
848
849
850
851
852
853
854
855
856
857
858
859
860

861
862

863
864
865
866
867
868
869
870

871
872
873
874
875
876
877
878







-
+










-
+














-
+

-
+














-
+

-
+







-
+







        debug!("  nxt_mod ={}", self.nxt_mod);
        debug!("  expected={}", self.expected_progress);
        Ok(())
    }


    /// Reduce size of bundle.
    fn compress_bundle(&mut self) -> Result<(), Error> {
    fn compress_bundle(&mut self) -> Result<(), SolverError> {
        for i in 0..self.problem.num_subproblems() {
            let n = self.master.num_minorants(i);
            if n >= self.params.max_bundle_size {
                // aggregate minorants with smallest coefficients
                self.minorants[i].sort_by_key(|m| -((1e6 * m.multiplier) as isize));
                let aggr = self.minorants[i].split_off(self.params.max_bundle_size - 2);
                let aggr_sum = aggr.iter().map(|m| m.multiplier).sum();
                let (aggr_mins, aggr_primals): (Vec<_>, Vec<_>) = aggr.into_iter()
                    .map(|m| (m.index, m.primal.unwrap()))
                    .unzip();
                let (aggr_min, aggr_coeffs) = try!(self.master.aggregate(i, &aggr_mins));
                let (aggr_min, aggr_coeffs) = self.master.aggregate(i, &aggr_mins).map_err(SolverError::Master)?;
                // append aggregated minorant
                self.minorants[i].push(MinorantInfo {
                    index: aggr_min,
                    multiplier: aggr_sum,
                    primal: Some(self.problem.aggregate_primals(aggr_coeffs.into_iter()
                        .zip(aggr_primals.into_iter())
                        .collect())),
                });
            }
        }
        Ok(())
    }

    /// Perform a descent step.
    fn descent_step(&mut self) -> Result<(), Error> {
    fn descent_step(&mut self) -> Result<(), SolverError> {
        let new_weight = self.weighter.weight(&current_state!(self, Step::Descent), &self.params);
        self.master.set_weight(new_weight)?;
        self.master.set_weight(new_weight).map_err(SolverError::Master)?;
        self.cnt_descent += 1;
        swap(&mut self.cur_y, &mut self.nxt_y);
        swap(&mut self.cur_val, &mut self.nxt_val);
        swap(&mut self.cur_mod, &mut self.nxt_mod);
        swap(&mut self.cur_vals, &mut self.nxt_vals);
        swap(&mut self.cur_mods, &mut self.nxt_mods);
        self.master.move_center(1.0, &self.nxt_d);
        debug!("Descent Step");
        debug!("  dir ={}", self.nxt_d);
        debug!("  newy={}", self.cur_y);
        Ok(())
    }

    /// Perform a null step.
    fn null_step(&mut self) -> Result<(), Error> {
    fn null_step(&mut self) -> Result<(), SolverError> {
        let new_weight = self.weighter.weight(&current_state!(self, Step::Null), &self.params);
        self.master.set_weight(new_weight)?;
        self.master.set_weight(new_weight).map_err(SolverError::Master)?;
        self.cnt_null += 1;
        debug!("Null Step");
        Ok(())
    }

    /// Perform one bundle iteration.
    #[cfg_attr(feature = "cargo-clippy", allow(collapsible_if))]
    pub fn step(&mut self) -> Result<Step, Error> {
    pub fn step(&mut self) -> Result<Step, SolverError> {
        self.iterinfos.clear();

        if !self.cur_valid {
            // current point needs new evaluation
            try!(self.init_master());
        }

887
888
889
890
891
892
893

894


895
896
897
898
899
900
901
902
903
904
905

906
907
908
909
910
911
912
913
914
915
916
917

918
919
920
921
922
923
924
896
897
898
899
900
901
902
903

904
905
906
907
908
909
910
911
912
913
914
915

916
917
918
919
920
921
922
923
924
925
926
927

928
929
930
931
932
933
934
935







+
-
+
+










-
+











-
+








        try!(self.compress_bundle());

        let mut nxt_lb = 0.0;
        let mut nxt_ub = 0.0;
        self.new_cutval = 0.0;
        for fidx in 0..self.problem.num_subproblems() {
            let result = self.problem
            let result = self.problem.evaluate(fidx, &self.nxt_y, nullstep_bnd, relprec)?;
                .evaluate(fidx, &self.nxt_y, nullstep_bnd, relprec)
                .map_err(SolverError::Evaluation)?;
            let fun_ub = result.objective();

            let mut minorants = result.into_iter();
            let mut nxt_minorant;
            let nxt_primal;
            match minorants.next() {
                Some((m, p)) => {
                    nxt_minorant = m;
                    nxt_primal = p;
                }
                None => return Err(SolverError::NoMinorant.into()),
                None => return Err(SolverError::NoMinorant),
            }
            let fun_lb = nxt_minorant.constant;

            nxt_lb += fun_lb;
            nxt_ub += fun_ub;
            self.nxt_vals[fidx] = fun_ub;

            // move center of minorant to cur_y
            nxt_minorant.move_center(-1.0, &self.nxt_d);
            self.new_cutval += nxt_minorant.constant;
            self.minorants[fidx].push(MinorantInfo {
                index: try!(self.master.add_minorant(fidx, nxt_minorant)),
                index: self.master.add_minorant(fidx, nxt_minorant).map_err(SolverError::Master)?,
                multiplier: 0.0,
                primal: Some(nxt_primal),
            });
        }

        if self.new_cutval > self.cur_val + 1e-3 {
            warn!("New minorant has higher value in center new:{} old:{}",