ROL
ROL_PrimalDualRisk.hpp
Go to the documentation of this file.
1 // @HEADER
2 // ************************************************************************
3 //
4 // Rapid Optimization Library (ROL) Package
5 // Copyright (2014) Sandia Corporation
6 //
7 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
8 // license for use of this work by or on behalf of the U.S. Government.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // Questions? Contact lead developers:
38 // Drew Kouri (dpkouri@sandia.gov) and
39 // Denis Ridzal (dridzal@sandia.gov)
40 //
41 // ************************************************************************
42 // @HEADER
43 
44 #ifndef ROL_PRIMALDUALRISK_H
45 #define ROL_PRIMALDUALRISK_H
46 
50 #include "ROL_PD_CVaR.hpp"
51 #include "ROL_PD_BPOE.hpp"
52 #include "ROL_PD_HMCR2.hpp"
53 #include "ROL_RiskVector.hpp"
56 #include "ROL_Types.hpp"
57 
58 namespace ROL {
59 
60 template <class Real>
62 private:
63  const Ptr<OptimizationProblem<Real>> input_;
64  const Ptr<SampleGenerator<Real>> sampler_;
65  Ptr<PD_RandVarFunctional<Real>> rvf_;
66  ParameterList parlist_;
67  // General algorithmic parameters
68  int maxit_;
69  bool print_;
70  Real gtolmin_;
71  Real ctolmin_;
72  Real ltolmin_;
73  Real tolupdate_;
74  // Subproblem solver tolerances
75  Real gtol_;
76  Real ctol_;
77  Real ltol_;
78  // Penalty parameter information
80  Real maxPen_;
81  // Forced udpate information
82  Real update_;
83  int freq_;
84 
85  Ptr<StochasticObjective<Real>> pd_objective_;
86  Ptr<Vector<Real>> pd_vector_;
87  Ptr<BoundConstraint<Real>> pd_bound_;
88  Ptr<Constraint<Real>> pd_constraint_;
89  Ptr<OptimizationProblem<Real>> pd_problem_;
90 
92  bool converged_;
93  Real lnorm_;
94  std::string name_;
95 
96 public:
98  const Ptr<SampleGenerator<Real>> &sampler,
99  ParameterList &parlist)
100  : input_(input), sampler_(sampler), parlist_(parlist),
101  iter_(0), converged_(true), lnorm_(ROL_INF<Real>()) {
102  // Get status test information
103  maxit_ = parlist.sublist("SOL").sublist("Primal Dual Risk").get("Iteration Limit",100);
104  print_ = parlist.sublist("SOL").sublist("Primal Dual Risk").get("Print Subproblem Solve History",false);
105  gtolmin_ = parlist.sublist("Status Test").get("Gradient Tolerance", 1e-8);
106  ctolmin_ = parlist.sublist("Status Test").get("Constraint Tolerance", 1e-8);
107  ltolmin_ = parlist.sublist("SOL").sublist("Primal Dual Risk").get("Dual Tolerance",1e-6);
108  gtolmin_ = (gtolmin_ <= static_cast<Real>(0) ? std::sqrt(ROL_EPSILON<Real>()) : gtolmin_);
109  ctolmin_ = (ctolmin_ <= static_cast<Real>(0) ? std::sqrt(ROL_EPSILON<Real>()) : ctolmin_);
110  ltolmin_ = (ltolmin_ <= static_cast<Real>(0) ? 1e2*std::sqrt(ROL_EPSILON<Real>()) : ltolmin_);
111  // Get solver tolerances
112  gtol_ = parlist.sublist("SOL").sublist("Primal Dual Risk").get("Initial Gradient Tolerance", 1e-4);
113  ctol_ = parlist.sublist("SOL").sublist("Primal Dual Risk").get("Initial Constraint Tolerance", 1e-4);
114  ltol_ = parlist.sublist("SOL").sublist("Primal Dual Risk").get("Initial Dual Tolerance", 1e-2);
115  tolupdate_ = parlist.sublist("SOL").sublist("Primal Dual Risk").get("Solver Tolerance Update Scale", 1e-1);
116  gtol_ = std::max(gtol_, gtolmin_);
117  ctol_ = std::max(ctol_, ctolmin_);
118  ltol_ = std::max(ltol_, ltolmin_);
119  // Get penalty parameter
120  penaltyParam_ = parlist.sublist("SOL").sublist("Primal Dual Risk").get("Initial Penalty Parameter", 10.0);
121  maxPen_ = parlist.sublist("SOL").sublist("Primal Dual Risk").get("Maximum Penalty Parameter", -1.0);
122  update_ = parlist.sublist("SOL").sublist("Primal Dual Risk").get("Penalty Update Scale", 10.0);
123  maxPen_ = (maxPen_ <= static_cast<Real>(0) ? ROL_INF<Real>() : maxPen_);
124  penaltyParam_ = std::min(penaltyParam_,maxPen_);
125  // Get update parameters
126  freq_ = parlist.sublist("SOL").sublist("Primal Dual Risk").get("Update Frequency", 0);
127  // Create risk vector and risk-averse objective
128  std::string type = parlist.sublist("SOL").get("Stochastic Component Type", "Risk Averse");
129  if (type == "Risk Averse") {
130  name_ = parlist.sublist("SOL").sublist("Risk Measure").get("Name","CVaR");
131  }
132  else if (type == "Probability") {
133  name_ = parlist.sublist("SOL").sublist("Probability"). get("Name","bPOE");
134  }
135  else {
136  std::stringstream message;
137  message << ">>> " << type << " is not implemented!";
138  throw Exception::NotImplemented(message.str());
139  }
140  Ptr<ParameterList> parlistptr = makePtrFromRef<ParameterList>(parlist);
141  if (name_ == "CVaR") {
142  parlistptr->sublist("SOL").set("Stochastic Component Type","Risk Averse");
143  parlistptr->sublist("SOL").sublist("Risk Measure").set("Name","CVaR");
144  Real alpha = parlist.sublist("SOL").sublist("Risk Measure").sublist("CVaR").get("Convex Combination Parameter", 1.0);
145  Real beta = parlist.sublist("SOL").sublist("Risk Measure").sublist("CVaR").get("Confidence Level", 0.9);
146  rvf_ = makePtr<PD_CVaR<Real>>(alpha, beta);
147  }
148  else if (name_ == "Mean Plus Semi-Deviation") {
149  parlistptr->sublist("SOL").set("Stochastic Component Type","Risk Averse");
150  parlistptr->sublist("SOL").sublist("Risk Measure").set("Name","Mean Plus Semi-Deviation");
151  Real coeff = parlist.sublist("SOL").sublist("Risk Measure").sublist("Mean Plus Semi-Deviation").get("Coefficient", 0.5);
152  rvf_ = makePtr<PD_MeanSemiDeviation<Real>>(coeff);
153  }
154  else if (name_ == "Mean Plus Semi-Deviation From Target") {
155  parlistptr->sublist("SOL").set("Stochastic Component Type","Risk Averse");
156  parlistptr->sublist("SOL").sublist("Risk Measure").set("Name","Mean Plus Semi-Deviation From Target");
157  Real coeff = parlist.sublist("SOL").sublist("Risk Measure").sublist("Mean Plus Semi-Deviation From Target").get("Coefficient", 0.5);
158  Real target = parlist.sublist("SOL").sublist("Risk Measure").sublist("Mean Plus Semi-Deviation From Target").get("Target", 1.0);
159  rvf_ = makePtr<PD_MeanSemiDeviationFromTarget<Real>>(coeff, target);
160  }
161  else if (name_ == "HMCR") {
162  parlistptr->sublist("SOL").set("Stochastic Component Type","Risk Averse");
163  parlistptr->sublist("SOL").sublist("Risk Measure").set("Name","HMCR");
164  //Real alpha = parlist.sublist("SOL").sublist("Risk Measure").sublist("HMCR").get("Convex Combination Parameter", 1.0);
165  Real beta = parlist.sublist("SOL").sublist("Risk Measure").sublist("HMCR").get("Confidence Level", 0.9);
166  rvf_ = makePtr<PD_HMCR2<Real>>(beta);
167  }
168  else if (name_ == "bPOE") {
169  parlistptr->sublist("SOL").set("Stochastic Component Type","Probability");
170  parlistptr->sublist("SOL").sublist("Probability").set("Name","bPOE");
171  Real thresh = parlist.sublist("SOL").sublist("Probability").sublist("bPOE").get("Threshold", 1.0);
172  rvf_ = makePtr<PD_BPOE<Real>>(thresh);
173  }
174  else {
175  std::stringstream message;
176  message << ">>> " << name_ << " is not implemented!";
177  throw Exception::NotImplemented(message.str());
178  }
179  pd_vector_ = makePtr<RiskVector<Real>>(parlistptr,
180  input_->getSolutionVector());
181  rvf_->setData(*sampler_, penaltyParam_);
182  pd_objective_ = makePtr<StochasticObjective<Real>>(input_->getObjective(),
183  rvf_, sampler_, true);
184  // Create risk bound constraint
185  pd_bound_ = makePtr<RiskBoundConstraint<Real>>(parlistptr,
186  input_->getBoundConstraint());
187  // Create riskless constraint
188  pd_constraint_ = nullPtr;
189  if (input_->getConstraint() != nullPtr) {
190  pd_constraint_ = makePtr<RiskLessConstraint<Real>>(input_->getConstraint());
191  }
192  // Build primal-dual subproblems
193  pd_problem_ = makePtr<OptimizationProblem<Real>>(pd_objective_,
194  pd_vector_,
195  pd_bound_,
197  input_->getMultiplierVector());
198  }
199 
200  void check(std::ostream &outStream = std::cout) {
201  pd_problem_->check(outStream);
202  }
203 
204  void run(std::ostream &outStream = std::cout) {
205  int spiter(0);
206  iter_ = 0; converged_ = true; lnorm_ = ROL_INF<Real>();
207  nfval_ = 0; ncval_ = 0; ngrad_ = 0;
208  // Output
209  printHeader(outStream);
210  Ptr<OptimizationSolver<Real>> solver;
211  for (iter_ = 0; iter_ < maxit_; ++iter_) {
212  parlist_.sublist("Status Test").set("Gradient Tolerance", gtol_);
213  parlist_.sublist("Status Test").set("Constraint Tolerance", ctol_);
214  solver = makePtr<OptimizationSolver<Real>>(*pd_problem_, parlist_);
215  if (print_) {
216  solver->solve(outStream);
217  }
218  else {
219  solver->solve();
220  }
221  converged_ = (solver->getAlgorithmState()->statusFlag == EXITSTATUS_CONVERGED
222  ||solver->getAlgorithmState()->statusFlag == EXITSTATUS_USERDEFINED
223  ? true : false);
224  spiter += solver->getAlgorithmState()->iter;
225  nfval_ += solver->getAlgorithmState()->nfval;
226  ngrad_ += solver->getAlgorithmState()->ngrad;
227  ncval_ += solver->getAlgorithmState()->ncval;
228  lnorm_ = rvf_->computeDual(*sampler_);
229  // Output
230  print(*solver->getAlgorithmState(),outStream);
231  // Check termination conditions
232  if (checkStatus(*solver->getAlgorithmState(),outStream)) {
233  break;
234  }
235  // Update penalty parameter and solver tolerances
236  rvf_->updateDual(*sampler_);
237  if (converged_) {
238  if (lnorm_ > ltol_ || (freq_ > 0 && iter_%freq_ == 0)) {
240  rvf_->updatePenalty(penaltyParam_);
241  }
242  if (lnorm_ <= ltol_) {
243  ltol_ = std::max(tolupdate_*ltol_, ltolmin_);
244  }
245  gtol_ = std::max(tolupdate_*gtol_, gtolmin_);
246  ctol_ = std::max(tolupdate_*ctol_, ctolmin_);
247  }
248  }
249  input_->getSolutionVector()->set(
250  *dynamicPtrCast<RiskVector<Real>>(pd_problem_->getSolutionVector())->getVector());
251  // Output reason for termination
252  if (iter_ >= maxit_) {
253  outStream << "Maximum number of iterations exceeded" << std::endl;
254  }
255  outStream << "Primal Dual Risk required " << spiter
256  << " subproblem iterations" << std::endl;
257  }
258 
259 private:
260  void printHeader(std::ostream &outStream) const {
261  std::ios_base::fmtflags flags = outStream.flags();
262  outStream << std::scientific << std::setprecision(6);
263  outStream << std::endl << "Primal Dual Risk Minimization using "
264  << name_ << std::endl << " "
265  << std::setw(8) << std::left << "iter"
266  << std::setw(15) << std::left << "value";
267  if (pd_constraint_ != nullPtr) {
268  outStream << std::setw(15) << std::left << "cnorm";
269  }
270  outStream << std::setw(15) << std::left << "gnorm"
271  << std::setw(15) << std::left << "lnorm"
272  << std::setw(15) << std::left << "penalty";
273  if (pd_constraint_ != nullPtr) {
274  outStream << std::setw(15) << std::left << "ctol";
275  }
276  outStream << std::setw(15) << std::left << "gtol"
277  << std::setw(15) << std::left << "ltol"
278  << std::setw(10) << std::left << "nfval"
279  << std::setw(10) << std::left << "ngrad";
280  if (pd_constraint_ != nullPtr) {
281  outStream << std::setw(10) << std::left << "ncval";
282  }
283  outStream << std::setw(10) << std::left << "subiter"
284  << std::setw(8) << std::left << "success"
285  << std::endl;
286  outStream.setf(flags);
287  }
288 
289  void print(const AlgorithmState<Real> &state, std::ostream &outStream) const {
290  std::ios_base::fmtflags flags = outStream.flags();
291  outStream << std::scientific << std::setprecision(6);
292  outStream << " "
293  << std::setw(8) << std::left << iter_+1
294  << std::setw(15) << std::left << state.value;
295  if (pd_constraint_ != nullPtr) {
296  outStream << std::setw(15) << std::left << state.cnorm;
297  }
298  outStream << std::setw(15) << std::left << state.gnorm
299  << std::setw(15) << std::left << lnorm_
300  << std::setw(15) << std::left << penaltyParam_;
301  if (pd_constraint_ != nullPtr) {
302  outStream << std::setw(15) << std::left << ctol_;
303  }
304  outStream << std::setw(15) << std::left << gtol_
305  << std::setw(15) << std::left << ltol_
306  << std::setw(10) << std::left << nfval_
307  << std::setw(10) << std::left << ngrad_;
308  if (pd_constraint_ != nullPtr) {
309  outStream << std::setw(10) << std::left << ncval_;
310  }
311  outStream << std::setw(10) << std::left << state.iter
312  << std::setw(8) << std::left << converged_
313  << std::endl;
314  outStream.setf(flags);
315  }
316 
317  bool checkStatus(const AlgorithmState<Real> &state, std::ostream &outStream) const {
318  bool flag = false;
319 // if (converged_ && state.iter==0 && lnorm_ < tol) {
320 // outStream << "Subproblem solve converged in zero iterations"
321 // << " and the difference in the multipliers was less than "
322 // << tol1 << std::endl;
323 // flag = true;
324 // }
325  if (pd_constraint_ == nullPtr) {
326  if (state.gnorm < gtolmin_ && lnorm_ < ltolmin_) {
327  outStream << "Solver tolerance met"
328  << " and the difference in the multipliers was less than "
329  << ltolmin_ << std::endl;
330  flag = true;
331  }
332  }
333  else {
334  if (state.gnorm < gtolmin_ && state.cnorm < ctolmin_ && lnorm_ < ltolmin_) {
335  outStream << "Solver tolerance met"
336  << " and the difference in the multipliers was less than "
337  << ltolmin_ << std::endl;
338  flag = true;
339  }
340  }
341  return flag;
342  }
343 
344 }; // class PrimalDualRisk
345 
346 } // namespace ROL
347 
348 #endif
Ptr< PD_RandVarFunctional< Real > > rvf_
Ptr< Vector< Real > > pd_vector_
Real ROL_INF(void)
Definition: ROL_Types.hpp:105
void printHeader(std::ostream &outStream) const
Contains definitions of custom data types in ROL.
void run(std::ostream &outStream=std::cout)
void print(const AlgorithmState< Real > &state, std::ostream &outStream) const
const Ptr< OptimizationProblem< Real > > input_
State for algorithm class. Will be used for restarts.
Definition: ROL_Types.hpp:143
bool checkStatus(const AlgorithmState< Real > &state, std::ostream &outStream) const
Ptr< Constraint< Real > > pd_constraint_
const Ptr< SampleGenerator< Real > > sampler_
Ptr< StochasticObjective< Real > > pd_objective_
void check(std::ostream &outStream=std::cout)
Ptr< OptimizationProblem< Real > > pd_problem_
PrimalDualRisk(const Ptr< OptimizationProblem< Real >> &input, const Ptr< SampleGenerator< Real >> &sampler, ParameterList &parlist)
Ptr< BoundConstraint< Real > > pd_bound_