int main(){
  // For all problems
  enum Problems{ODE,DAE,NUM_PROBLEMS};
  for(int problem=0; problem<NUM_PROBLEMS; ++problem){
    
    // Get problem
    FX ffcn;              // Callback function
    vector<double> x0;    // Initial value
    double u0;            // Parameter value
    double tf;            // End time
    switch(problem){
      case ODE:
        cout << endl << "** Testing ODE example **" << endl;
        simpleODE(ffcn,tf,x0,u0);
        break;
      case DAE:
        cout << endl << "** Testing DAE example **" << endl;
        simpleDAE(ffcn,tf,x0,u0);
        break;
    }
    
    // For all integrators
    enum Integrators{CVODES,IDAS,COLLOCATION,NUM_INTEGRATORS};
    for(int integrator=0; integrator<NUM_INTEGRATORS; ++integrator){

      // Get integrator
      Integrator I;
      switch(integrator){
        case CVODES:
          if(problem==DAE) continue; // Skip if DAE
          cout << endl << "== CVodesIntegrator == " << endl;
          I = CVodesIntegrator(ffcn);
          break;
        case IDAS:
          cout << endl << "== IdasIntegrator == " << endl;
          I = IdasIntegrator(ffcn);
          break;
        case COLLOCATION:
          cout << endl << "== CollocationIntegrator == " << endl;
          I = CollocationIntegrator(ffcn);
          
          // Set collocation integrator specific options
          I.setOption("expand_f",true);
          I.setOption("implicit_solver",KinsolSolver::creator);
          Dictionary kinsol_options;
          kinsol_options["linear_solver"] = CSparse::creator;
          I.setOption("implicit_solver_options",kinsol_options);
          break;
      }
      
      // Set common options
      I.setOption("tf",tf);
      
      // Initialize the integrator
      I.init();
      
      // Integrate to get results
      I.setInput(x0,INTEGRATOR_X0);
      I.setInput(u0,INTEGRATOR_P);
      I.evaluate();
      DMatrix xf = I.output(INTEGRATOR_XF);
      DMatrix qf = I.output(INTEGRATOR_QF);
      cout << setw(50) << "Unperturbed solution: " << "xf  = " << xf <<  ", qf  = " << qf << endl;

      // Perturb solution to get a finite difference approximation
      double h = 0.001;
      I.setInput(u0+h,INTEGRATOR_P);
      I.evaluate();
      DMatrix xf_pert = I.output(INTEGRATOR_XF);
      DMatrix qf_pert = I.output(INTEGRATOR_QF);
      cout << setw(50) << "Finite difference approximation: " << "d(xf)/d(p) = " << (xf_pert-xf)/h << ", d(qf)/d(p) = " << (qf_pert-qf)/h << endl;

      // Operator overloading approach
      I.setInput(x0,INTEGRATOR_X0);
      I.setInput(u0,INTEGRATOR_P);
      I.setFwdSeed(0.,INTEGRATOR_X0);
      I.setFwdSeed(1.,INTEGRATOR_P);
      I.reset(1,0,0);
      I.integrate(tf);
      DMatrix oo_xf = I.fwdSens(INTEGRATOR_XF);
      DMatrix oo_qf = I.fwdSens(INTEGRATOR_QF);
      cout << setw(50) << "Forward sensitivities via OO: " << "d(xf)/d(p) = " << oo_xf << ", d(qf)/d(p) = " << oo_qf << endl;

      // Calculate once, forward
      FX I_fwd = I.derivative(1,0);
      I_fwd.setInput(x0,INTEGRATOR_X0);
      I_fwd.setInput(u0,INTEGRATOR_P);
      I_fwd.setInput(0.,INTEGRATOR_NUM_IN+INTEGRATOR_X0);
      I_fwd.setInput(1.,INTEGRATOR_NUM_IN+INTEGRATOR_P);
      I_fwd.evaluate();
      DMatrix fwd_xf = I_fwd.output(INTEGRATOR_NUM_OUT+INTEGRATOR_XF);
      DMatrix fwd_qf = I_fwd.output(INTEGRATOR_NUM_OUT+INTEGRATOR_QF);
      cout << setw(50) << "Forward sensitivities: " << "d(xf)/d(p) = " << fwd_xf << ", d(qf)/d(p) = " << fwd_qf << endl;

      // Calculate once, adjoint
      FX I_adj = I.derivative(0,1);
      I_adj.setInput(x0,INTEGRATOR_X0);
      I_adj.setInput(u0,INTEGRATOR_P);
      I_adj.setInput(0.,INTEGRATOR_NUM_IN+INTEGRATOR_XF);
      I_adj.setInput(1.,INTEGRATOR_NUM_IN+INTEGRATOR_QF);
      I_adj.evaluate();
      DMatrix adj_x0 = I_adj.output(INTEGRATOR_NUM_OUT+INTEGRATOR_X0);
      DMatrix adj_p = I_adj.output(INTEGRATOR_NUM_OUT+INTEGRATOR_P);
      cout << setw(50) << "Adjoint sensitivities: " << "d(qf)/d(x0) = " << adj_x0 << ", d(qf)/d(p) = " << adj_p << endl;

      // Perturb adjoint solution to get a finite difference approximation of the second order sensitivities
      I_adj.setInput(x0,INTEGRATOR_X0);
      I_adj.setInput(u0+h,INTEGRATOR_P);
      I_adj.setInput(0.,INTEGRATOR_NUM_IN+INTEGRATOR_XF);
      I_adj.setInput(1.,INTEGRATOR_NUM_IN+INTEGRATOR_QF);
      I_adj.evaluate();
      DMatrix adj_x0_pert = I_adj.output(INTEGRATOR_NUM_OUT+INTEGRATOR_X0);
      DMatrix adj_p_pert = I_adj.output(INTEGRATOR_NUM_OUT+INTEGRATOR_P);
      cout << setw(50) << "FD of adjoint sensitivities: " << "d2(qf)/d(x0)d(p) = " << (adj_x0_pert-adj_x0)/h << ", d2(qf)/d(p)d(p) = " << (adj_p_pert-adj_p)/h << endl;

      // Forward over adjoint to get the second order sensitivities
      I_adj.setInput(x0,INTEGRATOR_X0);
      I_adj.setInput(u0,INTEGRATOR_P);
      I_adj.setFwdSeed(1.,INTEGRATOR_P);
      I_adj.setInput(0.,INTEGRATOR_NUM_IN+INTEGRATOR_XF);
      I_adj.setInput(1.,INTEGRATOR_NUM_IN+INTEGRATOR_QF);
      I_adj.evaluate(1,0);
      DMatrix fwd_adj_x0 = I_adj.fwdSens(INTEGRATOR_NUM_OUT+INTEGRATOR_X0);
      DMatrix fwd_adj_p = I_adj.fwdSens(INTEGRATOR_NUM_OUT+INTEGRATOR_P);
      cout << setw(50) << "Forward over adjoint sensitivities: " << "d2(qf)/d(x0)d(p) = " << fwd_adj_x0 << ", d2(qf)/d(p)d(p) = " << fwd_adj_p << endl;

      // Adjoint over adjoint to get the second order sensitivities
      I_adj.setInput(x0,INTEGRATOR_X0);
      I_adj.setInput(u0,INTEGRATOR_P);
      I_adj.setInput(0.,INTEGRATOR_NUM_IN+INTEGRATOR_XF);
      I_adj.setInput(1.,INTEGRATOR_NUM_IN+INTEGRATOR_QF);
      I_adj.setAdjSeed(1.,INTEGRATOR_NUM_OUT+INTEGRATOR_P);
      I_adj.evaluate(0,1);
      DMatrix adj_adj_x0 = I_adj.adjSens(INTEGRATOR_X0);
      DMatrix adj_adj_p = I_adj.adjSens(INTEGRATOR_P);
      cout << setw(50) << "Adjoint over adjoint sensitivities: " << "d2(qf)/d(x0)d(p) = " << adj_adj_x0 << ", d2(qf)/d(p)d(p) = " << adj_adj_p << endl;
    }
  }
  return 0;
}
Exemple #2
0
int main(){
  // Time horizon
  double t0 = 0,  tf = 10;
  
  // Bounds on the control
  double /*u_lb = -0.5, u_ub = 1.3,*/ u_init = 1;

  // Initial conditions
  vector<double> x0(3);
  x0[0] = 0;
  x0[1] = 0;
  x0[2] = 1;
  
  // Integrator
  Integrator integrator = create_Sundials();
  
  // Attach user-defined linear solver
  if(user_defined_solver){
    if(sparse_direct){
      integrator.setOption("linear_solver_creator",CSparse::creator);
      // integrator.setOption("linear_solver_creator",SuperLU::creator);
    } else {
      integrator.setOption("linear_solver_creator",LapackLUDense::creator);
      integrator.setOption("linear_solver","user_defined");
    }
    // integrator.setOption("linear_solver","user_defined"); // FIXME: bug for second order
  }
  
  // Set common integrator options
  integrator.setOption("fsens_err_con",true);
  integrator.setOption("quad_err_con",true);
  integrator.setOption("abstol",1e-12);
  integrator.setOption("reltol",1e-12);
  integrator.setOption("fsens_abstol",1e-6);
  integrator.setOption("fsens_reltol",1e-6);
  integrator.setOption("asens_abstol",1e-6);
  integrator.setOption("asens_reltol",1e-6);
  integrator.setOption("exact_jacobian",exact_jacobian);
  integrator.setOption("finite_difference_fsens",finite_difference_fsens);
  integrator.setOption("max_num_steps",100000);
//  integrator.setOption("max_multistep_order",4);
  integrator.setOption("t0",t0);
  integrator.setOption("tf",tf);

  // Initialize the integrator
  integrator.init();
 
  // Set parameters
  integrator.setInput(u_init,INTEGRATOR_P);
  
  // Set inital state
  integrator.setInput(x0,INTEGRATOR_X0);
  
  // Integrate
  integrator.evaluate();

  // Save the result
  Matrix<double> res0_xf = integrator.output(INTEGRATOR_XF);
  Matrix<double> res0_qf = integrator.output(INTEGRATOR_QF);

  // Perturb in some direction
  if(perturb_u){
    double u_pert = u_init + 0.01;
    integrator.setInput(u_pert,INTEGRATOR_P);
  } else {
    vector<double> x_pert = x0;
    x_pert[1] += 0.01;
    integrator.setInput(x_pert,INTEGRATOR_X0);
  }
  
  // Integrate again
  integrator.evaluate();
  
  // Print statistics
  integrator.printStats();

  // Calculate finite difference approximation
  Matrix<double> fd_xf = (integrator.output(INTEGRATOR_XF) - res0_xf)/0.01;
  Matrix<double> fd_qf = (integrator.output(INTEGRATOR_QF) - res0_qf)/0.01;

  cout << "unperturbed                     " << res0_xf << "; " << res0_qf << endl;
  cout << "perturbed                       " << integrator.output(INTEGRATOR_XF) << "; " << integrator.output(INTEGRATOR_QF) << endl;
  cout << "finite_difference approximation " << fd_xf << "; " << fd_qf << endl;

  if(perturb_u){
    integrator.setFwdSeed(1.0,INTEGRATOR_P);
  } else {
    vector<double> x0_seed(x0.size(),0);
    x0_seed[1] = 1;
    integrator.setFwdSeed(x0_seed,INTEGRATOR_X0);
  }
    
  // Reset parameters
  integrator.setInput(u_init,INTEGRATOR_P);
  
  // Reset initial state
  integrator.setInput(x0,INTEGRATOR_X0);

  if(with_asens){
    // backward seeds
    vector<double> &bseed = integrator.adjSeed(INTEGRATOR_XF).data();
    fill(bseed.begin(),bseed.end(),0);
    bseed[1] = 1;

    // evaluate with forward and adjoint sensitivities
    integrator.evaluate(1,1);
  } else {
    // evaluate with only forward sensitivities
    integrator.evaluate(1,0);
  }
    
  Matrix<double> fsens_xf = integrator.fwdSens(INTEGRATOR_XF);
  Matrix<double> fsens_qf = integrator.fwdSens(INTEGRATOR_QF);
  cout << "forward sensitivities           " << fsens_xf << "; " << fsens_qf << endl;

  if(with_asens){
    cout << "adjoint sensitivities           ";
    cout << integrator.adjSens(INTEGRATOR_X0) << "; ";
    cout << integrator.adjSens(INTEGRATOR_P) << "; ";
    cout << endl;
  }
  
  if(second_order){
    // Preturb the forward seeds
    if(perturb_u){
      double u_seed = 1.001;
      integrator.setFwdSeed(u_seed,INTEGRATOR_P);
    } else {
      vector<double> x0_seed(x0.size(),0);
      x0_seed[1] = 1.001;
      integrator.setFwdSeed(x0_seed,INTEGRATOR_X0);
    }
    
    // evaluate again with forward sensitivities
    integrator.evaluate(1,0);

    vector<double> fsens_pret_xf = integrator.fwdSens(INTEGRATOR_XF).data();
    vector<double> fsens_pret_qf = integrator.fwdSens(INTEGRATOR_QF).data();
    cout << "forward sensitivities preturbed " << fsens_pret_xf << "; " << fsens_pret_qf << endl;

    vector<double> fd2_xf(fsens_xf.size());
    vector<double> fd2_qf(fsens_qf.size());
    for(int i=0; i<fd2_xf.size(); ++i)
      fd2_xf[i] = (fsens_pret_xf.at(i)-fsens_xf.at(i))/0.001;
    for(int i=0; i<fd2_qf.size(); ++i)
      fd2_qf[i] = (fsens_pret_qf.at(i)-fsens_qf.at(i))/0.001;
    
    cout << "finite differences, 2nd order   " << fd2_xf << "; " << fd2_qf << endl;
    
    // Generate the jacobian by creating a new integrator for the sensitivity equations by source transformation
    FX intjac  = integrator.jacobian(INTEGRATOR_P,INTEGRATOR_XF);

    // Set options
    intjac.setOption("number_of_fwd_dir",0);
    intjac.setOption("number_of_adj_dir",1);
    
    // Initialize the integrator
    intjac.init();

    // Set inputs
    intjac.setInput(u_init,INTEGRATOR_P);
    intjac.setInput(x0,INTEGRATOR_X0);
        
    // Set adjoint seed
    vector<double> jacseed(3*1,0);
    jacseed[0] = 1;
    intjac.setAdjSeed(jacseed);
    
    // Evaluate the Jacobian
    intjac.evaluate(0,0);

    
    cout << "jacobian                  " << intjac.output(0) << endl;
    
    // Get the results
/*    cout << "unperturbed via jacobian        " << intjac.output(1+INTEGRATOR_XF) << endl;*/
    cout << "second order (fwd-over-adj)     " ;
    cout << intjac.adjSens(INTEGRATOR_X0) << ", ";
    cout << intjac.adjSens(INTEGRATOR_P) << endl;

    // Save the unpreturbed value
    Matrix<double> unpret = intjac.output();
    
    // Perturb X0
    intjac.setInput(u_init+0.01,INTEGRATOR_P);

    intjac.evaluate();
    Matrix<double> pret = intjac.output();
    
    cout << "unperturbed fwd sens            " << unpret << endl;
    cout << "perturbed fwd sens              " << pret << endl;
    cout << "finite diff. (augmented dae)    " << (pret-unpret)/0.01 << endl;
    
  }
  
  return 0;
}