MUQ  0.4.3
MonotoneExpansion.cpp
Go to the documentation of this file.
3 
6 
7 #include <memory>
8 
9 using namespace muq::Approximation;
10 using namespace muq::Utilities;
11 
12 MonotoneExpansion::MonotoneExpansion(std::shared_ptr<BasisExpansion> monotonePartsIn,
13  bool coeffInput) : MonotoneExpansion({std::make_shared<BasisExpansion>(std::vector<std::shared_ptr<IndexedScalarBasis>>({std::make_shared<Monomial>()}))},
14  {monotonePartsIn},
15  coeffInput)
16 {
17 }
18 
19 MonotoneExpansion::MonotoneExpansion(std::vector<std::shared_ptr<BasisExpansion>> const& generalPartsIn,
20  std::vector<std::shared_ptr<BasisExpansion>> const& monotonePartsIn,
21  bool coeffInput) : ModPiece(GetInputSizes(monotonePartsIn, generalPartsIn, coeffInput),
22  monotonePartsIn.size()*Eigen::VectorXi::Ones(1)),
23  generalParts(generalPartsIn),
24  monotoneParts(monotonePartsIn)
25 {
26  assert(generalPartsIn.size() == monotonePartsIn.size());
27  assert(generalPartsIn.at(0)->Multis()->GetMaxOrders().maxCoeff()==0);
28 
29  numInputs = -1;
30 
31  // Get the maximum order of of the monotone parts
32  int maxOrder = 0;
33  for(int i=0; i<monotoneParts.size(); ++i)
34  maxOrder = std::max(maxOrder, monotoneParts.at(i)->Multis()->GetMaxOrders()(i) );
35 
36  // Number of quadrature points is based on the fact that an N point Gauss-quadrature
37  // rule can integrate exactly a polynomial of order 2N-1
38  int numQuadPts = ceil(0.5*(2.0*maxOrder + 1.0));
39  GaussQuadrature gqSolver(std::make_shared<Legendre>(), numQuadPts-1);
40  gqSolver.Compute(numQuadPts-1);
41 
42  quadPts = 0.5*(gqSolver.Points().transpose()+Eigen::VectorXd::Ones(numQuadPts));
43  quadWeights = 0.5*gqSolver.Weights();
44 }
45 
46 std::shared_ptr<MonotoneExpansion> MonotoneExpansion::Head(int numRows) const
47 {
48  assert(numRows<=generalParts.size());
49 
50  std::vector<std::shared_ptr<BasisExpansion>> newGenerals(numRows);
51  std::vector<std::shared_ptr<BasisExpansion>> newMonotones(numRows);
52 
53  for(int i=0; i<numRows; ++i){
54  newGenerals.at(i) = generalParts.at(i);
55  newMonotones.at(i) = monotoneParts.at(i);
56  }
57 
58  return std::make_shared<MonotoneExpansion>(newGenerals, newMonotones);
59 }
60 
61 Eigen::VectorXd MonotoneExpansion::EvaluateInverse(Eigen::VectorXd const& refPt) const{
62  Eigen::VectorXd tgtPt0 = Eigen::VectorXd::Zero(refPt.size());
63  return EvaluateInverse(refPt, tgtPt0);
64 }
65 
66 Eigen::VectorXd MonotoneExpansion::EvaluateInverse(Eigen::VectorXd const& refPt,
67  Eigen::VectorXd const& tgtPt0) const{
68 
69  Eigen::VectorXd tgtPt = tgtPt0;
70  Eigen::VectorXd mappedPt, resid, step, newPt;
71  double residNorm = 1e4;
72  double newResidNorm;
73  const int maxLineIts = 20;
74 
75  while(residNorm>1e-11){
76  resid = EvaluateForward(tgtPt) - refPt;
77  step = JacobianWrtX(tgtPt).triangularView<Eigen::Lower>().solve(resid);
78 
79  double stepSize = 1.0;
80  newPt = tgtPt - stepSize * step;
81 
82  for(int lineIt=0; lineIt<maxLineIts; ++lineIt){
83  newResidNorm = (EvaluateForward(newPt) - refPt).norm();
84  if(newResidNorm<(1.0-1e-9)*residNorm)
85  break;
86  stepSize *= 0.5;
87  newPt = tgtPt - stepSize * step;
88  }
89 
90  residNorm = newResidNorm;
91  tgtPt = newPt;
92  }
93 
94  return tgtPt;
95 }
96 
97 
98 unsigned MonotoneExpansion::NumTerms() const{
99 
100  unsigned numTerms = 0;
101  for(int i=0; i<generalParts.size(); ++i)
102  numTerms += generalParts.at(i)->NumTerms();
103  for(int i=0; i<monotoneParts.size(); ++i)
104  numTerms += monotoneParts.at(i)->NumTerms();
105 
106  return numTerms;
107 }
108 
109 Eigen::VectorXd MonotoneExpansion::GetCoeffs() const
110 {
111  Eigen::VectorXd output(NumTerms());
112 
113  unsigned currInd = 0;
114 
115  for(int i=0; i<generalParts.size(); ++i){
116  output.segment(currInd, generalParts.at(i)->NumTerms()) = generalParts.at(i)->GetCoeffs().transpose();
117  currInd += generalParts.at(i)->NumTerms();
118  }
119 
120  for(int i=0; i<monotoneParts.size(); ++i){
121  output.segment(currInd, monotoneParts.at(i)->NumTerms()) = monotoneParts.at(i)->GetCoeffs().transpose();
122  currInd += monotoneParts.at(i)->NumTerms();
123  }
124 
125  return output;
126 }
127 
128 void MonotoneExpansion::SetCoeffs(Eigen::VectorXd const& allCoeffs)
129 {
130  unsigned currInd = 0;
131 
132  for(int i=0; i<generalParts.size(); ++i){
133  generalParts.at(i)->SetCoeffs(allCoeffs.segment(currInd, generalParts.at(i)->NumTerms()).transpose());
134  currInd += generalParts.at(i)->NumTerms();
135  }
136 
137  for(int i=0; i<monotoneParts.size(); ++i){
138  monotoneParts.at(i)->SetCoeffs(allCoeffs.segment(currInd, monotoneParts.at(i)->NumTerms()).transpose());
139  currInd += monotoneParts.at(i)->NumTerms();
140  }
141 }
142 
143 Eigen::VectorXd MonotoneExpansion::EvaluateForward(Eigen::VectorXd const& x) const{
144 
145  Eigen::VectorXd refPt = Eigen::VectorXd::Zero(x.size());
146 
147  // Fill in the general parts
148  for(int i=0; i<generalParts.size(); ++i)
149  refPt(i) += generalParts.at(i)->Evaluate(x).at(0)(0);
150 
151  // Now add the monotone part
152  Eigen::VectorXd quadEvals(quadPts.size());
153  for(int i=0; i<monotoneParts.size(); ++i){
154 
155  // Loop over quadrature points
156  Eigen::VectorXd evalPt = x;//.head(i+1);
157  evalPt(i) = x(i) * quadPts(0);
158 
159  double polyEval = monotoneParts.at(i)->Evaluate(evalPt).at(0)(0);
160 
161  quadEvals(0) = polyEval*polyEval;
162  for(int k=1; k<quadPts.size(); ++k){
163  evalPt(i) = x(i) * quadPts(k);
164  polyEval = monotoneParts.at(i)->Evaluate(evalPt).at(0)(0);
165  quadEvals(k) = polyEval*polyEval;
166  }
167  refPt(i) += quadEvals.dot(quadWeights)*x(i);
168  }
169 
170  return refPt;
171 }
172 void MonotoneExpansion::EvaluateImpl(muq::Modeling::ref_vector<Eigen::VectorXd> const& inputs)
173 {
174  // Update the coefficients if need be
175  if(inputs.size()>1){
176  SetCoeffs(inputs.at(1).get());
177  }
178 
179  outputs.resize(1);
180  outputs.at(0) = EvaluateForward(inputs.at(0).get());
181 }
182 
183 
184 Eigen::MatrixXd MonotoneExpansion::JacobianWrtX(Eigen::VectorXd const& x) const{
185 
186  Eigen::MatrixXd jac = Eigen::MatrixXd::Zero(x.size(), x.size());
187 
188  double polyEval;
189  Eigen::MatrixXd polyGrad;
190 
191  // Add all of the general parts
192  for(int i=0; i<generalParts.size(); ++i){
193  Eigen::MatrixXd partialJac = generalParts.at(i)->Jacobian(0,0,x);
194  jac.block(i,0,1,i) += partialJac.block(0,0,1,i);
195  }
196 
197  // Add the monotone parts
198  for(int i=0; i<monotoneParts.size(); ++i){
199  Eigen::VectorXd evalPt = x;//.head(i+1);
200 
201  for(int k=0; k<quadPts.size(); ++k){
202  evalPt(i) = x(i) * quadPts(k);
203  polyEval = monotoneParts.at(i)->Evaluate(evalPt).at(0)(0);
204  polyGrad = monotoneParts.at(i)->Jacobian(0,0,evalPt);
205  jac.block(i,0,1,i) += 2.0 * x(i) * quadWeights(k) * polyEval * polyGrad.block(0,0,1,i);
206  jac(i,i) += quadWeights(k) * (polyEval * polyEval + 2.0*x(i)*polyGrad(i)*polyEval*quadPts(k));
207  }
208  }
209 
210  return jac;
211 }
212 
213 
214 void MonotoneExpansion::JacobianImpl(unsigned int const wrtIn,
215  unsigned int const wrtOut,
217 {
218  if(inputs.size()>1){
219  SetCoeffs(inputs.at(1).get());
220  }
221 
222  Eigen::VectorXd const& x = inputs.at(0);
223  assert(x.size()==monotoneParts.size());
224 
225  Eigen::MatrixXd jac;
226  if(wrtIn==0){
227 
228  jac = JacobianWrtX(x);
229 
230  }else if(wrtIn==1){
231 
232  // calculate the total number of coefficients
233  unsigned numTerms = NumTerms();
234 
235  // Initialize the jacobian to zero
236  jac = Eigen::MatrixXd::Zero(x.size(), numTerms);
237 
238  // Fill in the general portion of the jacobian
239  unsigned currCoeff = 0;
240  for(int i=0; i<generalParts.size(); ++i){
241  Eigen::MatrixXd partialJac = generalParts.at(i)->Jacobian(0,1,x);
242  jac.block(i, currCoeff, 1, generalParts.at(i)->NumTerms()) += partialJac;
243  currCoeff += generalParts.at(i)->NumTerms();
244  }
245 
246  // Fill in the monotone portion of the jacobian
247  double polyEval;
248  Eigen::MatrixXd polyGrad;
249  for(int i=0; i<monotoneParts.size(); ++i){
250  Eigen::VectorXd evalPt = x;//.head(i+1);
251 
252  for(int k=0; k<quadPts.size(); ++k){
253  evalPt(i) = x(i) * quadPts(k);
254  polyEval = monotoneParts.at(i)->Evaluate(evalPt).at(0)(0);
255  polyGrad = monotoneParts.at(i)->Jacobian(0,1,evalPt);
256  jac.block(i,currCoeff,1,monotoneParts.at(i)->NumTerms()) += 2.0 * x(i) * quadWeights(k) * polyEval * polyGrad;
257  }
258 
259  currCoeff += monotoneParts.at(i)->NumTerms();
260  }
261 
262  }
263 
264  jacobian = jac;
265 }
266 
267 
274 double MonotoneExpansion::LogDeterminant(Eigen::VectorXd const& evalPt,
275  Eigen::VectorXd const& coeffs)
276 {
277  SetCoeffs(coeffs);
278  return LogDeterminant(evalPt);
279 }
280 double MonotoneExpansion::LogDeterminant(Eigen::VectorXd const& evalPt)
281 {
282  assert(evalPt.rows() == inputSizes(0));
284  vecIn.push_back(std::cref(evalPt));
285  JacobianImpl(0,0,vecIn);
286  return jacobian.diagonal().array().log().sum();
287 }
288 
290 Eigen::VectorXd MonotoneExpansion::GradLogDeterminant(Eigen::VectorXd const& evalPt,
291  Eigen::VectorXd const& coeffs)
292 {
293  SetCoeffs(coeffs);
294  return GradLogDeterminant(evalPt);
295 }
296 
297 Eigen::VectorXd MonotoneExpansion::GradLogDeterminant(Eigen::VectorXd const& x)
298 {
299 
300  Eigen::VectorXd output = Eigen::VectorXd::Zero(NumTerms());
301 
302  double polyEval;
303  Eigen::MatrixXd polyGradX, polyGradC, polyD2;
304 
305  // Figure out what the first monotone coefficient is
306  unsigned currInd = 0;
307  for(int i=0; i<generalParts.size(); ++i)
308  currInd += generalParts.at(i)->NumTerms();
309 
310  // Add the monotone parts to the gradient
311  for(int i=0; i<monotoneParts.size(); ++i){
312  Eigen::VectorXd evalPt = x;//.head(i+1);
313 
314  double part1 = 0.0;
315  Eigen::VectorXd part2 = Eigen::VectorXd::Zero(monotoneParts.at(i)->NumTerms());
316 
317  for(int k=0; k<quadPts.size(); ++k){
318  evalPt(i) = x(i) * quadPts(k);
319  polyEval = monotoneParts.at(i)->Evaluate(evalPt).at(0)(0);
320  polyGradX = monotoneParts.at(i)->Jacobian(0,0,evalPt);
321  polyGradC = monotoneParts.at(i)->Jacobian(0,1,evalPt);
322  polyD2 = monotoneParts.at(i)->SecondDerivative(0, 0, 1, evalPt);
323 
324  part1 += quadWeights(k) * (polyEval * polyEval + 2.0*x(i)*polyGradX(i)*polyEval*quadPts(k));
325  part2 += 2.0 * quadWeights(k) * (polyEval * polyGradC + x(i) * quadPts(k) * (polyEval*polyD2.row(i) + polyGradX(i)*polyGradC)).transpose();
326  }
327 
328  output.segment(currInd, monotoneParts.at(i)->NumTerms()) = part2/part1;
329 
330  currInd += monotoneParts.at(i)->NumTerms();
331 
332  }
333 
334  return output;
335 }
336 
337 
338 Eigen::VectorXi MonotoneExpansion::GetInputSizes(std::vector<std::shared_ptr<BasisExpansion>> const& generalPartsIn,
339  std::vector<std::shared_ptr<BasisExpansion>> const& monotonePartsIn,
340  bool coeffInput)
341 {
342  Eigen::VectorXi output;
343  if(coeffInput){
344 
345  int numCoeffs = 0;
346  for(int i=0; i<generalPartsIn.size(); ++i)
347  numCoeffs += generalPartsIn.at(i)->NumTerms();
348  for(int i=0; i<monotonePartsIn.size(); ++i)
349  numCoeffs += monotonePartsIn.at(i)->NumTerms();
350 
351  output.resize(2);
352  output << monotonePartsIn.size(), numCoeffs;
353 
354  }else{
355  output.resize(1);
356  output << monotonePartsIn.size();
357  }
358  return output;
359 }
Class for computing Gauss Quadrature rules from an orthogonal polynomial family.
std::vector< std::reference_wrapper< const T > > ref_vector
A vector of references to something ...
Definition: WorkPiece.h:37