ROL
ROL_BundleStep.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// Rapid Optimization Library (ROL) Package
4//
5// Copyright 2014 NTESS and the ROL contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef ROL_BUNDLE_STEP_H
11#define ROL_BUNDLE_STEP_H
12
13#include "ROL_Bundle_AS.hpp"
14#include "ROL_Bundle_TT.hpp"
15#include "ROL_Types.hpp"
16#include "ROL_Step.hpp"
17#include "ROL_Vector.hpp"
18#include "ROL_Objective.hpp"
20#include "ROL_LineSearch.hpp"
21
22#include "ROL_ParameterList.hpp"
23#include "ROL_Ptr.hpp"
24
30namespace ROL {
31
32template <class Real>
33class BundleStep : public Step<Real> {
34private:
35 // Bundle
36 ROL::Ptr<Bundle<Real> > bundle_; // Bundle of subgradients and linearization errors
37 ROL::Ptr<LineSearch<Real> > lineSearch_; // Line-search object for nonconvex problems
38
39 // Dual cutting plane solution
40 unsigned QPiter_; // Number of QP solver iterations
41 unsigned QPmaxit_; // Maximum number of QP iterations
42 Real QPtol_; // QP subproblem tolerance
43
44 // Step flag
45 int step_flag_; // Whether serious or null step
46
47 // Additional storage
48 ROL::Ptr<Vector<Real> > y_;
49
50 // Updated iterate storage
53
54 // Aggregate subgradients, linearizations, and distance measures
55 ROL::Ptr<Vector<Real> > aggSubGradNew_; // New aggregate subgradient
56 Real aggSubGradOldNorm_; // Old aggregate subgradient norm
57 Real aggLinErrNew_; // New aggregate linearization error
58 Real aggLinErrOld_; // Old aggregate linearization error
59 Real aggDistMeasNew_; // New aggregate distance measure
60
61 // Algorithmic parameters
62 Real T_;
63 Real tol_;
64 Real m1_;
65 Real m2_;
66 Real m3_;
67 Real nu_;
68
69 // Line-search parameters
71
74
75 Real ftol_;
76
78
79public:
80
81 using Step<Real>::initialize;
82 using Step<Real>::compute;
83 using Step<Real>::update;
84
85 BundleStep(ROL::ParameterList &parlist)
86 : bundle_(ROL::nullPtr), lineSearch_(ROL::nullPtr),
87 QPiter_(0), QPmaxit_(0), QPtol_(0), step_flag_(0),
88 y_(ROL::nullPtr), linErrNew_(0), valueNew_(0),
91 T_(0), tol_(0), m1_(0), m2_(0), m3_(0), nu_(0),
92 ls_maxit_(0), first_print_(true), isConvex_(false),
93 ftol_(ROL_EPSILON<Real>()) {
94 Real zero(0), two(2), oem3(1.e-3), oem6(1.e-6), oem8(1.e-8), p1(0.1), p2(0.2), p9(0.9), oe3(1.e3), oe8(1.e8);
95 ROL::Ptr<StepState<Real> > state = Step<Real>::getState();
96 state->searchSize = parlist.sublist("Step").sublist("Bundle").get("Initial Trust-Region Parameter", oe3);
97 T_ = parlist.sublist("Step").sublist("Bundle").get("Maximum Trust-Region Parameter", oe8);
98 tol_ = parlist.sublist("Step").sublist("Bundle").get("Epsilon Solution Tolerance", oem6);
99 m1_ = parlist.sublist("Step").sublist("Bundle").get("Upper Threshold for Serious Step", p1);
100 m2_ = parlist.sublist("Step").sublist("Bundle").get("Lower Threshold for Serious Step", p2);
101 m3_ = parlist.sublist("Step").sublist("Bundle").get("Upper Threshold for Null Step", p9);
102 nu_ = parlist.sublist("Step").sublist("Bundle").get("Tolerance for Trust-Region Parameter", oem3);
103
104 // Initialize bundle
105 Real coeff = parlist.sublist("Step").sublist("Bundle").get("Distance Measure Coefficient", zero);
106 Real omega = parlist.sublist("Step").sublist("Bundle").get("Locality Measure Coefficient", two);
107 unsigned maxSize = parlist.sublist("Step").sublist("Bundle").get("Maximum Bundle Size", 200);
108 unsigned remSize = parlist.sublist("Step").sublist("Bundle").get("Removal Size for Bundle Update", 2);
109 int cps = parlist.sublist("Step").sublist("Bundle").get("Cutting Plane Solver",0);
110 if (cps) {
111 bundle_ = ROL::makePtr<Bundle_TT<Real>>(maxSize,coeff,omega,remSize);
112 //bundle_ = ROL::makePtr<Bundle_AS<Real>>(maxSize,coeff,omega,remSize);
113 }
114 else {
115 bundle_ = ROL::makePtr<Bundle_AS<Real>>(maxSize,coeff,omega,remSize);
116 }
117 isConvex_ = ((coeff == zero) ? true : false);
118
119 // Initialize QP solver
120 QPtol_ = parlist.sublist("Step").sublist("Bundle").get("Cutting Plane Tolerance", oem8);
121 QPmaxit_ = parlist.sublist("Step").sublist("Bundle").get("Cutting Plane Iteration Limit", 1000);
122
123 // Initialize Line Search
125 = parlist.sublist("Step").sublist("Line Search").get("Maximum Number of Function Evaluations",20);
126 if ( !isConvex_ ) {
127 lineSearch_ = LineSearchFactory<Real>(parlist);
128 }
129
130 // Get verbosity level
131 verbosity_ = parlist.sublist("General").get("Print Verbosity", 0);
132 }
133
136 AlgorithmState<Real> &algo_state ) {
137 // Call default initializer, but maintain current searchSize
138 ROL::Ptr<StepState<Real> > state = Step<Real>::getState();
139 Real searchSize = state->searchSize;
140 Step<Real>::initialize(x,x,g,obj,con,algo_state);
141 state->searchSize = searchSize;
142 // Initialize bundle
143 bundle_->initialize(*(state->gradientVec));
144 // Initialize storage for updated iterate
145 y_ = x.clone();
146 // Initialize storage for aggregate subgradients
147 aggSubGradNew_ = g.clone();
148 aggSubGradOldNorm_ = algo_state.gnorm;
149 // Initialize line search
150 if ( !isConvex_ ) {
151 lineSearch_->initialize(x,x,g,obj,con);
152 }
153 }
154
156 BoundConstraint<Real> &con, AlgorithmState<Real> &algo_state ) {
157 ROL::Ptr<StepState<Real> > state = Step<Real>::getState();
158 first_print_ = false; // Print header only on first serious step
159 QPiter_ = (step_flag_==1 ? 0 : QPiter_); // Reset QPiter only on serious steps
160 Real v(0), l(0), u = T_, gd(0); // Scalar storage
161 Real zero(0), two(2), half(0.5);
162 bool flag = true;
163 while (flag) {
164 /*************************************************************/
165 /******** Solve Dual Cutting Plane QP Problem ****************/
166 /*************************************************************/
167 QPiter_ += bundle_->solveDual(state->searchSize,QPmaxit_,QPtol_); // Solve QP subproblem
168 bundle_->aggregate(*aggSubGradNew_,aggLinErrNew_,aggDistMeasNew_); // Compute aggregate info
169 algo_state.aggregateGradientNorm = aggSubGradNew_->norm(); // Aggregate subgradient norm
170 if (verbosity_ > 0) {
171 std::cout << std::endl;
172 std::cout << " Computation of aggregrate quantities" << std::endl;
173 std::cout << " Aggregate subgradient norm: " << algo_state.aggregateGradientNorm << std::endl;
174 std::cout << " Aggregate linearization error: " << aggLinErrNew_ << std::endl;
175 std::cout << " Aggregate distance measure: " << aggDistMeasNew_ << std::endl;
176 }
177 /*************************************************************/
178 /******** Construct Cutting Plane Solution *******************/
179 /*************************************************************/
180 v = -state->searchSize*std::pow(algo_state.aggregateGradientNorm,two)-aggLinErrNew_; // CP objective value
181 s.set(aggSubGradNew_->dual()); s.scale(-state->searchSize); // CP solution
182 algo_state.snorm = state->searchSize*algo_state.aggregateGradientNorm; // Step norm
183 if (verbosity_ > 0) {
184 std::cout << std::endl;
185 std::cout << " Solve cutting plan subproblem" << std::endl;
186 std::cout << " Cutting plan objective value: " << v << std::endl;
187 std::cout << " Norm of computed step: " << algo_state.snorm << std::endl;
188 std::cout << " 'Trust-region' radius: " << state->searchSize << std::endl;
189 }
190 /*************************************************************/
191 /******** Decide Whether Step is Serious or Null *************/
192 /*************************************************************/
193 if (std::max(algo_state.aggregateGradientNorm,aggLinErrNew_) <= tol_) {
194 // Current iterate is already epsilon optimal!
195 s.zero(); algo_state.snorm = zero;
196 flag = false;
197 step_flag_ = 1;
198 algo_state.flag = true;
199 break;
200 }
201 else if (std::isnan(algo_state.aggregateGradientNorm)
202 || std::isnan(aggLinErrNew_)
203 || (std::isnan(aggDistMeasNew_) && !isConvex_)) {
204 s.zero(); algo_state.snorm = zero;
205 flag = false;
206 step_flag_ = 2;
207 algo_state.flag = true;
208 }
209 else {
210 // Current iterate is not epsilon optimal.
211 y_->set(x); y_->plus(s); // y is the candidate iterate
212 obj.update(*y_,true,algo_state.iter); // Update objective at y
213 valueNew_ = obj.value(*y_,ftol_); // Compute objective value at y
214 algo_state.nfval++;
215 obj.gradient(*(state->gradientVec),*y_,ftol_); // Compute objective (sub)gradient at y
216 algo_state.ngrad++;
217 // Compute new linearization error and distance measure
218 gd = s.dot(state->gradientVec->dual());
219 linErrNew_ = algo_state.value - (valueNew_ - gd); // Linearization error
220 // Determine whether to take a serious or null step
221 Real eps = static_cast<Real>(10)*ROL_EPSILON<Real>();
222 Real del = eps*std::max(static_cast<Real>(1),std::abs(algo_state.value));
223 Real Df = (valueNew_ - algo_state.value) - del;
224 Real Dm = v - del;
225 bool SS1 = false;
226 if (std::abs(Df) < eps && std::abs(Dm) < eps) {
227 SS1 = true;
228 }
229 else {
230 SS1 = (Df < m1_*Dm);
231 }
232 //bool SS1 = (valueNew_-algo_state.value < m1_*v);
233 //bool NS1 = (valueNew_-algo_state.value >= m1_*v);
234 bool NS2a = (bundle_->computeAlpha(algo_state.snorm,linErrNew_) <= m3_*aggLinErrOld_);
235 bool NS2b = (std::abs(algo_state.value-valueNew_) <= aggSubGradOldNorm_ + aggLinErrOld_);
236 if (verbosity_ > 0) {
237 std::cout << std::endl;
238 std::cout << " Check for serious/null step" << std::endl;
239 std::cout << " Serious step test SS(i): " << SS1 << std::endl;
240 std::cout << " -> Left hand side: " << valueNew_-algo_state.value << std::endl;
241 std::cout << " -> Right hand side: " << m1_*v << std::endl;
242 std::cout << " Null step test NS(iia): " << NS2a << std::endl;
243 std::cout << " -> Left hand side: " << bundle_->computeAlpha(algo_state.snorm,linErrNew_) << std::endl;
244 std::cout << " -> Right hand side: " << m3_*aggLinErrOld_ << std::endl;
245 std::cout << " Null step test NS(iib): " << NS2b << std::endl;
246 std::cout << " -> Left hand side: " << std::abs(algo_state.value-valueNew_) << std::endl;
247 std::cout << " -> Right hand side: " << aggSubGradOldNorm_ + aggLinErrOld_ << std::endl;
248 }
249 if ( isConvex_ ) {
250 /************* Convex objective ****************/
251 if ( SS1 ) {
252 bool SS2 = (gd >= m2_*v || state->searchSize >= T_-nu_);
253 if (verbosity_ > 0) {
254 std::cout << " Serious step test SS(iia): " << (gd >= m2_*v) << std::endl;
255 std::cout << " -> Left hand side: " << gd << std::endl;
256 std::cout << " -> Right hand side: " << m2_*v << std::endl;
257 std::cout << " Serious step test SS(iia): " << (state->searchSize >= T_-nu_) << std::endl;
258 std::cout << " -> Left hand side: " << state->searchSize << std::endl;
259 std::cout << " -> Right hand side: " << T_-nu_ << std::endl;
260 }
261 if ( SS2 ) { // Serious Step
262 step_flag_ = 1;
263 flag = false;
264 if (verbosity_ > 0) {
265 std::cout << " Serious step taken" << std::endl;
266 }
267 break;
268 }
269 else { // Increase trust-region radius
270 l = state->searchSize;
271 state->searchSize = half*(u+l);
272 if (verbosity_ > 0) {
273 std::cout << " Increase 'trust-region' radius: " << state->searchSize << std::endl;
274 }
275 }
276 }
277 else {
278 if ( NS2a || NS2b ) { // Null step
279 s.zero(); algo_state.snorm = zero;
280 step_flag_ = 0;
281 flag = false;
282 if (verbosity_ > 0) {
283 std::cout << " Null step taken" << std::endl;
284 }
285 break;
286 }
287 else { // Decrease trust-region radius
288 u = state->searchSize;
289 state->searchSize = half*(u+l);
290 if (verbosity_ > 0) {
291 std::cout << " Decrease 'trust-region' radius: " << state->searchSize << std::endl;
292 }
293 }
294 }
295 }
296 else {
297 /************* Nonconvex objective *************/
298 bool NS3 = (gd - bundle_->computeAlpha(algo_state.snorm,linErrNew_) >= m2_*v);
299 if (verbosity_ > 0) {
300 std::cout << " Null step test NS(iii): " << NS3 << std::endl;
301 std::cout << " -> Left hand side: " << gd - bundle_->computeAlpha(algo_state.snorm,linErrNew_) << std::endl;
302 std::cout << " -> Right hand side: " << m2_*v << std::endl;
303 }
304 if ( SS1 ) { // Serious step
305 step_flag_ = 1;
306 flag = false;
307 break;
308 }
309 else {
310 if ( NS2a || NS2b ) {
311 if ( NS3 ) { // Null step
312 s.zero();
313 step_flag_ = 0;
314 flag = false;
315 break;
316 }
317 else {
318 if ( NS2b ) { // Line search
319 Real alpha = zero;
320 int ls_nfval = 0, ls_ngrad = 0;
321 lineSearch_->run(alpha,valueNew_,ls_nfval,ls_ngrad,gd,s,x,obj,con);
322 if ( ls_nfval == ls_maxit_ ) { // Null step
323 s.zero();
324 step_flag_ = 0;
325 flag = false;
326 break;
327 }
328 else { // Serious step
329 s.scale(alpha);
330 step_flag_ = 1;
331 flag = false;
332 break;
333 }
334 }
335 else { // Decrease trust-region radius
336 u = state->searchSize;
337 state->searchSize = half*(u+l);
338 }
339 }
340 }
341 else { // Decrease trust-region radius
342 u = state->searchSize;
343 state->searchSize = half*(u+l);
344 }
345 }
346 }
347 }
348 } // End While
349 /*************************************************************/
350 /******** Update Algorithm State *****************************/
351 /*************************************************************/
355 } // End Compute
356
358 BoundConstraint<Real> &con, AlgorithmState<Real> &algo_state ) {
359 ROL::Ptr<StepState<Real> > state = Step<Real>::getState();
360 state->flag = step_flag_;
361 state->SPiter = QPiter_;
362 if ( !algo_state.flag ) {
363 /*************************************************************/
364 /******** Reset Bundle If Maximum Size Reached ***************/
365 /*************************************************************/
366 bundle_->reset(*aggSubGradNew_,aggLinErrNew_,algo_state.snorm);
367 /*************************************************************/
368 /******** Update Bundle with Step Information ****************/
369 /*************************************************************/
370 if ( step_flag_==1 ) {
371 // Serious step was taken
372 x.plus(s); // Update iterate
373 Real valueOld = algo_state.value; // Store previous objective value
374 algo_state.value = valueNew_; // Add new objective value to state
375 bundle_->update(step_flag_,valueNew_-valueOld,algo_state.snorm,*(state->gradientVec),s);
376 }
377 else if ( step_flag_==0 ) {
378 // Null step was taken
379 bundle_->update(step_flag_,linErrNew_,algo_state.snorm,*(state->gradientVec),s);
380 }
381 }
382 /*************************************************************/
383 /******** Update Algorithm State *****************************/
384 /*************************************************************/
385 algo_state.iterateVec->set(x);
386 algo_state.gnorm = (state->gradientVec)->norm();
387 if ( step_flag_==1 ) {
388 algo_state.iter++;
389 }
390 } // End Update
391
392 std::string printHeader( void ) const {
393 std::stringstream hist;
394 hist << " ";
395 hist << std::setw(6) << std::left << "iter";
396 hist << std::setw(15) << std::left << "value";
397 hist << std::setw(15) << std::left << "gnorm";
398 hist << std::setw(15) << std::left << "snorm";
399 hist << std::setw(10) << std::left << "#fval";
400 hist << std::setw(10) << std::left << "#grad";
401 hist << std::setw(15) << std::left << "znorm";
402 hist << std::setw(15) << std::left << "alpha";
403 hist << std::setw(15) << std::left << "TRparam";
404 hist << std::setw(10) << std::left << "QPiter";
405 hist << "\n";
406 return hist.str();
407 }
408
409 std::string printName( void ) const {
410 std::stringstream hist;
411 hist << "\n" << "Bundle Trust-Region Algorithm \n";
412 return hist.str();
413 }
414
415 std::string print( AlgorithmState<Real> &algo_state, bool print_header = false ) const {
416 const ROL::Ptr<const StepState<Real> > state = Step<Real>::getStepState();
417 std::stringstream hist;
418 hist << std::scientific << std::setprecision(6);
419 if ( algo_state.iter == 0 && first_print_ ) {
420 hist << printName();
421 if ( print_header ) {
422 hist << printHeader();
423 }
424 hist << " ";
425 hist << std::setw(6) << std::left << algo_state.iter;
426 hist << std::setw(15) << std::left << algo_state.value;
427 hist << std::setw(15) << std::left << algo_state.gnorm;
428 hist << "\n";
429 }
430 if ( step_flag_==1 && algo_state.iter > 0 ) {
431 if ( print_header ) {
432 hist << printHeader();
433 }
434 else {
435 hist << " ";
436 hist << std::setw(6) << std::left << algo_state.iter;
437 hist << std::setw(15) << std::left << algo_state.value;
438 hist << std::setw(15) << std::left << algo_state.gnorm;
439 hist << std::setw(15) << std::left << algo_state.snorm;
440 hist << std::setw(10) << std::left << algo_state.nfval;
441 hist << std::setw(10) << std::left << algo_state.ngrad;
442 hist << std::setw(15) << std::left << algo_state.aggregateGradientNorm;
443 hist << std::setw(15) << std::left << algo_state.aggregateModelError;
444 hist << std::setw(15) << std::left << state->searchSize;
445 hist << std::setw(10) << std::left << QPiter_;
446 hist << "\n";
447 }
448 }
449 return hist.str();
450 };
451
452}; // class BundleStep
453
454} // namespace ROL
455
456#endif
Objective_SerialSimOpt(const Ptr< Obj > &obj, const V &ui) z0_ zero()
Contains definitions of custom data types in ROL.
Provides the interface to apply upper and lower bound constraints.
Provides the interface to compute bundle trust-region steps.
void initialize(Vector< Real > &x, const Vector< Real > &g, Objective< Real > &obj, BoundConstraint< Real > &con, AlgorithmState< Real > &algo_state)
Initialize step with bound constraint.
void update(Vector< Real > &x, const Vector< Real > &s, Objective< Real > &obj, BoundConstraint< Real > &con, AlgorithmState< Real > &algo_state)
Update step, if successful.
ROL::Ptr< Vector< Real > > aggSubGradNew_
std::string printName(void) const
Print step name.
std::string printHeader(void) const
Print iterate header.
ROL::Ptr< Bundle< Real > > bundle_
std::string print(AlgorithmState< Real > &algo_state, bool print_header=false) const
Print iterate status.
ROL::Ptr< LineSearch< Real > > lineSearch_
BundleStep(ROL::ParameterList &parlist)
void compute(Vector< Real > &s, const Vector< Real > &x, Objective< Real > &obj, BoundConstraint< Real > &con, AlgorithmState< Real > &algo_state)
Compute step.
ROL::Ptr< Vector< Real > > y_
Provides the interface to evaluate objective functions.
virtual void gradient(Vector< Real > &g, const Vector< Real > &x, Real &tol)
Compute gradient.
virtual Real value(const Vector< Real > &x, Real &tol)=0
Compute value.
virtual void update(const Vector< Real > &x, UpdateType type, int iter=-1)
Update objective function.
Provides the interface to compute optimization steps.
Definition ROL_Step.hpp:34
virtual void initialize(Vector< Real > &x, const Vector< Real > &g, Objective< Real > &obj, BoundConstraint< Real > &con, AlgorithmState< Real > &algo_state)
Initialize step with bound constraint.
Definition ROL_Step.hpp:54
ROL::Ptr< StepState< Real > > getState(void)
Definition ROL_Step.hpp:39
const ROL::Ptr< const StepState< Real > > getStepState(void) const
Get state for step object.
Definition ROL_Step.hpp:177
Defines the linear algebra or vector space interface.
virtual void set(const Vector &x)
Set where .
virtual void scale(const Real alpha)=0
Compute where .
virtual void plus(const Vector &x)=0
Compute , where .
virtual void zero()
Set to zero vector.
virtual ROL::Ptr< Vector > clone() const =0
Clone to make a new (uninitialized) vector.
virtual Real dot(const Vector &x) const =0
Compute where .
Real ROL_EPSILON(void)
Platform-dependent machine epsilon.
Definition ROL_Types.hpp:57
State for algorithm class. Will be used for restarts.
ROL::Ptr< Vector< Real > > iterateVec