Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Merge trunk |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | async |
| Files: | files | file ages | folders |
| SHA1: |
f0a36f7482ed946cfc4b613ebb096d9a |
| User & Date: | fifr 2019-07-25 13:53:35.210 |
Context
|
2019-07-25
| ||
| 15:10 | Merge problem-update check-in: 25a20cb237 user: fifr tags: async | |
| 13:54 | Merge async check-in: 5f2a3800b8 user: fifr tags: problem-update | |
| 13:53 | Merge trunk check-in: f0a36f7482 user: fifr tags: async | |
| 13:46 | Update version to 0.6.0 check-in: b839aed06b user: fifr tags: release, v0.6.0 | |
| 10:31 | firstorderproblem: pass the subproblem index to `extend_subgradient` check-in: ff30e74d44 user: fifr tags: async | |
Changes
Changes to Cargo.toml.
1 2 | [package] name = "bundle" | | | 1 2 3 4 5 6 7 8 9 10 | [package] name = "bundle" version = "0.7.0-dev" edition = "2018" authors = ["Frank Fischer <frank-fischer@shadow-soft.de>"] [dependencies] itertools = "^0.8" libc = "^0.2.6" log = "^0.4" |
| ︙ | ︙ |
Changes to src/master/cpx.rs.
| ︙ | ︙ | |||
240 241 242 243 244 245 246 |
let mut changedvars = vec![];
changedvars.extend_from_slice(changed);
changedvars.extend(noldvars..nnewvars);
for (fidx, mins) in self.minorants.iter_mut().enumerate() {
if !mins.is_empty() {
for (i, m) in mins.iter_mut().enumerate() {
| | | | 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
let mut changedvars = vec![];
changedvars.extend_from_slice(changed);
changedvars.extend(noldvars..nnewvars);
for (fidx, mins) in self.minorants.iter_mut().enumerate() {
if !mins.is_empty() {
for (i, m) in mins.iter_mut().enumerate() {
let new_subg = extend_subgradient(fidx, self.min2index[fidx][i], &changedvars)
.map_err(CplexMasterError::SubgradientExtension)?;
for (&j, &g) in changed.iter().zip(new_subg.iter()) {
m.linear[j] = g;
}
m.linear.extend_from_slice(&new_subg[changed.len()..]);
}
}
}
|
| ︙ | ︙ |
Changes to src/solver.rs.
| ︙ | ︙ | |||
296 297 298 299 300 301 302 |
Descent,
/// No step but the algorithm has been terminated.
Term,
}
/// Information about a minorant.
#[derive(Debug, Clone)]
| | < < | > > | | | 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
Descent,
/// No step but the algorithm has been terminated.
Term,
}
/// Information about a minorant.
#[derive(Debug, Clone)]
struct MinorantInfo {
/// The minorant's index in the master problem
index: usize,
/// Current multiplier.
multiplier: Real,
}
/// Information about the last iteration.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum IterationInfo {
NewMinorantTooHigh { new: Real, old: Real },
UpperBoundNullStep,
ShallowCut,
}
/// State information for the update callback.
pub struct UpdateState<'a, Pr: 'a> {
/// Current model minorants.
minorants: &'a [Vec<MinorantInfo>],
/// The primals.
primals: &'a Vec<Option<Pr>>,
/// The last step type.
pub step: Step,
/// Iteration information.
pub iteration_info: &'a [IterationInfo],
/// The current candidate. If the step was a descent step, this is
/// the new center.
pub nxt_y: &'a DVector,
/// The center. IF the step was a descent step, this is the old
/// center.
pub cur_y: &'a DVector,
}
impl<'a, Pr: 'a> UpdateState<'a, Pr> {
pub fn aggregated_primals(&self, subproblem: usize) -> Vec<(Real, &Pr)> {
self.minorants[subproblem]
.iter()
.map(|m| (m.multiplier, self.primals[m.index].as_ref().unwrap()))
.collect()
}
/// Return the last primal for a given subproblem.
///
/// This is the last primal generated by the oracle.
pub fn last_primal(&self, fidx: usize) -> Option<&Pr> {
self.minorants[fidx].last().and_then(|m| self.primals[m.index].as_ref())
}
}
/// The default builder.
pub type FullMasterBuilder = master::boxed::Builder<master::cpx::Builder>;
/**
|
| ︙ | ︙ | |||
438 439 440 441 442 443 444 |
*/
start_time: Instant,
/// The master problem.
master: M::MasterProblem,
/// The active minorant indices for each subproblem.
| | > > > | 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 |
*/
start_time: Instant,
/// The master problem.
master: M::MasterProblem,
/// The active minorant indices for each subproblem.
minorants: Vec<Vec<MinorantInfo>>,
/// The primals associated with each global minorant index.
primals: Vec<Option<P::Primal>>,
/// Accumulated information about the last iteration.
iterinfos: Vec<IterationInfo>,
}
pub type Result<T, P, M> = std::result::Result<
T,
|
| ︙ | ︙ | |||
494 495 496 497 498 499 500 501 502 503 504 505 506 507 |
sgnorm: 0.0,
expected_progress: 0.0,
cnt_descent: 0,
cnt_null: 0,
start_time: Instant::now(),
master: M::default().build().map_err(|e| SolverError::BuildMaster(e.into()))?,
minorants: vec![],
iterinfos: vec![],
})
}
/// A new solver with default parameter.
#[allow(clippy::type_complexity)]
pub fn new(problem: P) -> Result<Solver<P, T, W, M>, P, M> {
| > | 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 |
sgnorm: 0.0,
expected_progress: 0.0,
cnt_descent: 0,
cnt_null: 0,
start_time: Instant::now(),
master: M::default().build().map_err(|e| SolverError::BuildMaster(e.into()))?,
minorants: vec![],
primals: vec![],
iterinfos: vec![],
})
}
/// A new solver with default parameter.
#[allow(clippy::type_complexity)]
pub fn new(problem: P) -> Result<Solver<P, T, W, M>, P, M> {
|
| ︙ | ︙ | |||
617 618 619 620 621 622 623 624 625 626 627 628 629 630 |
///
/// 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, P, M> {
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
| > | 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 |
///
/// 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, P, M> {
let updates = {
let state = UpdateState {
minorants: &self.minorants,
primals: &self.primals,
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
|
| ︙ | ︙ | |||
681 682 683 684 685 686 687 |
newvars.push((Some(index), lower - value, upper - value, value));
}
}
}
if !newvars.is_empty() {
let problem = &mut self.problem;
| | | | 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 |
newvars.push((Some(index), lower - value, upper - value, value));
}
}
}
if !newvars.is_empty() {
let problem = &mut self.problem;
let primals = &self.primals;
self.master.add_vars(
&newvars.iter().map(|v| (v.0, v.1, v.2)).collect::<Vec<_>>(),
&mut |fidx, minidx, vars| {
problem
.extend_subgradient(fidx, primals[minidx].as_ref().unwrap(), vars)
.map(DVector)
.map_err(|e| e.into())
},
)?;
// modify moved variables
for (index, val) in newvars.iter().filter_map(|v| v.0.map(|i| (i, v.3))) {
self.cur_y[index] = val;
|
| ︙ | ︙ | |||
717 718 719 720 721 722 723 |
/// with their coefficients $\alpha_i$. The aggregated primal can
/// be computed by combining the minorants $\bar{x} =
/// \sum_{i=1}\^m \alpha_i x_i$.
pub fn aggregated_primals(&self, subproblem: usize) -> P::Primal {
Aggregatable::combine(
self.minorants[subproblem]
.iter()
| | | 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 |
/// with their coefficients $\alpha_i$. The aggregated primal can
/// be computed by combining the minorants $\bar{x} =
/// \sum_{i=1}\^m \alpha_i x_i$.
pub fn aggregated_primals(&self, subproblem: usize) -> P::Primal {
Aggregatable::combine(
self.minorants[subproblem]
.iter()
.map(|m| (m.multiplier, self.primals[m.index].as_ref().unwrap())),
)
}
fn show_info(&self, step: Step) {
let time = self.start_time.elapsed();
info!(
"{} {:0>2}:{:0>2}:{:0>2}.{:0>2} {:4} {:4} {:4}{:1} {:9.4} {:9.4} \
|
| ︙ | ︙ | |||
799 800 801 802 803 804 805 806 |
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 {
| > | < > > > > | 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 |
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];
let minidx = self.master.add_minorant(i, minorant)?;
self.minorants[i].push(MinorantInfo {
index: minidx,
multiplier: 0.0,
});
if minidx >= self.primals.len() {
self.primals.resize_with(minidx + 1, || None);
}
self.primals[minidx] = Some(primal);
} else {
return Err(SolverError::NoMinorant);
}
}
self.cur_valid = true;
|
| ︙ | ︙ | |||
865 866 867 868 869 870 871 |
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();
| | | > > < > | 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 |
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, self.primals[m.index].take().unwrap()))
.unzip();
let (aggr_min, aggr_coeffs) = self.master.aggregate(i, &aggr_mins)?;
// append aggregated minorant
self.minorants[i].push(MinorantInfo {
index: aggr_min,
multiplier: aggr_sum,
});
self.primals[aggr_min] = Some(Aggregatable::combine(aggr_coeffs.into_iter().zip(&aggr_primals)));
}
}
Ok(())
}
/// Perform a descent step.
fn descent_step(&mut self) -> Result<(), P, M> {
|
| ︙ | ︙ | |||
956 957 958 959 960 961 962 963 |
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 {
| > | < > > > > | 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 |
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;
let minidx = self.master.add_minorant(fidx, nxt_minorant)?;
self.minorants[fidx].push(MinorantInfo {
index: minidx,
multiplier: 0.0,
});
if minidx >= self.primals.len() {
self.primals.resize_with(minidx + 1, || None);
}
self.primals[minidx] = Some(nxt_primal);
}
if self.new_cutval > self.cur_val + 1e-3 {
warn!(
"New minorant has higher value in center new:{} old:{}",
self.new_cutval, self.cur_val
);
|
| ︙ | ︙ |