finite_difference_Jacobian.cpp
Go to the documentation of this file.
1 // Copyright 2019, University of Maryland and the MANGO development team.
2 //
3 // This file is part of MANGO.
4 //
5 // MANGO is free software: you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License as
7 // published by the Free Software Foundation, either version 3 of the
8 // License, or (at your option) any later version.
9 //
10 // MANGO is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with MANGO. If not, see
17 // <https://www.gnu.org/licenses/>.
18 
19 #include <iostream>
20 #include <iomanip>
21 #include <cstring>
22 #include <cmath>
23 #include <ctime>
24 #include "mpi.h"
25 #include "mango.hpp"
26 #include "Solver.hpp"
27 
28 void mango::Solver::finite_difference_Jacobian(vector_function_type vector_function, int N_terms, const double* state_vector, double* base_case_residual_function, double* Jacobian) {
29 
30  // base_case_residual_function should have been allocated already, with size N_terms.
31  // Jacobian should have been allocated already, with size N_parameters * N_terms.
32 
33  // To simplify code in this file, make some copies of variables.
34  MPI_Comm mpi_comm_group_leaders = mpi_partition->get_comm_group_leaders();
35  bool proc0_world = mpi_partition->get_proc0_world();
36  int mpi_rank_world = mpi_partition->get_rank_world();
37 
38  int data;
39  int j_evaluation, j_parameter;
40 
41  if (verbose > 0) std::cout << "Hello from finite_difference_Jacobian from proc " << mpi_rank_world << std::endl;
42 
43  if (proc0_world) {
44  // Tell the group leaders to start this subroutine
45  data = 1;
46  MPI_Bcast(&data,1,MPI_INT,0,mpi_comm_group_leaders);
47  }
48 
49  // Only proc0_world has a meaningful state vector at this point.
50  // Copy it to the other processors. In the process we make a copy of the state vector,
51  // so the original state vector can be const.
52  double* state_vector_copy = new double[N_parameters];
53  if (proc0_world) memcpy(state_vector_copy, state_vector, N_parameters*sizeof(double));
54  MPI_Bcast(state_vector_copy, N_parameters, MPI_DOUBLE, 0, mpi_comm_group_leaders);
55 
56  int N_evaluations;
58  N_evaluations = N_parameters * 2+ 1;
59  } else {
60  N_evaluations = N_parameters + 1;
61  }
62 
63  double* perturbed_state_vector = new double[N_parameters];
64  double* residual_functions = new double[N_terms * N_evaluations];
65  double* state_vectors = new double[N_parameters * N_evaluations];
66 
67  memset(base_case_residual_function, 0, N_terms*sizeof(double));
68  memset(residual_functions, 0, N_terms * N_evaluations*sizeof(double));
69 
70  // Build the set of state vectors that will be considered.
71  for (j_evaluation = 1; j_evaluation <= N_evaluations; j_evaluation++) {
72  memcpy(perturbed_state_vector, state_vector_copy, N_parameters*sizeof(double));
73  if (j_evaluation == 1) {
74  // This is the base case, so do not perturb the state vector.
75  } else if (j_evaluation <= N_parameters + 1) {
76  // We are doing a forward step
77  perturbed_state_vector[j_evaluation - 2] = perturbed_state_vector[j_evaluation - 2] + finite_difference_step_size;
78  } else {
79  // We must be doing a backwards step
80  perturbed_state_vector[j_evaluation - 2 - N_parameters] = perturbed_state_vector[j_evaluation - 2 - N_parameters] - finite_difference_step_size;
81  }
82  memcpy(&state_vectors[(j_evaluation-1)*N_parameters], perturbed_state_vector, N_parameters*sizeof(double));
83  }
84 
85  if (proc0_world && (verbose > 0)) {
86  std::cout << "Here comes state_vectors:" << std::endl;
87  for(j_parameter=0; j_parameter<N_parameters; j_parameter++) {
88  for(j_evaluation=0; j_evaluation<N_evaluations; j_evaluation++) {
89  std::cout << std::setw(25) << std::setprecision(15) << state_vectors[j_evaluation*N_parameters + j_parameter];
90  }
91  std::cout << std::endl;
92  }
93  }
94 
95  // Each proc now evaluates the residual function for its share of the perturbed state vectors.
96  bool* failures = new bool[N_evaluations];
97  evaluate_set_in_parallel(vector_function, N_terms, N_evaluations, state_vectors, residual_functions, failures);
98  delete failures; // Eventually do something smarter with the failure data.
99 
100 
101  // Finally, evaluate the finite difference derivatives.
102  memcpy(base_case_residual_function, residual_functions, N_terms*sizeof(double));
103  if (centered_differences) {
104  for (j_parameter=0; j_parameter<N_parameters; j_parameter++) {
105  for (int j_term=0; j_term<N_terms; j_term++) {
106  Jacobian[j_parameter*N_terms+j_term] = (residual_functions[(j_parameter+1)*N_terms+j_term] - residual_functions[(j_parameter+1+N_parameters)*N_terms+j_term])
108  }
109  }
110  } else {
111  // 1-sided finite differences
112  for (j_parameter=0; j_parameter<N_parameters; j_parameter++) {
113  for (int j_term=0; j_term<N_terms; j_term++) {
114  Jacobian[j_parameter*N_terms+j_term] = (residual_functions[(j_parameter+1)*N_terms+j_term] - base_case_residual_function[j_term]) / finite_difference_step_size;
115  }
116  }
117  }
118 
119  // Clean up.
120  delete[] perturbed_state_vector;
121  delete[] residual_functions;
122  delete[] state_vectors;
123  delete[] state_vector_copy;
124 
125  if (proc0_world && (verbose > 0)) {
126  std::cout << "Here comes finite-difference Jacobian:" << std::endl;
127  for (int j_term=0; j_term<N_terms; j_term++) {
128  for (j_parameter=0; j_parameter<N_parameters; j_parameter++) {
129  std::cout << std::setw(25) << std::setprecision(15) << Jacobian[j_parameter*N_terms+j_term];
130  }
131  std::cout << std::endl;
132  }
133  }
134 
135 }
mango::Solver::centered_differences
bool centered_differences
Definition: Solver.hpp:59
mango::Solver::verbose
int verbose
Definition: Solver.hpp:63
mango::MPI_Partition::get_comm_group_leaders
MPI_Comm get_comm_group_leaders()
Get the MPI communicator for MANGO's "group leaders" communicator.
Definition: mpi_partition.cpp:51
Solver.hpp
mango::Solver::finite_difference_step_size
double finite_difference_step_size
Definition: Solver.hpp:60
mango::Solver::state_vector
double * state_vector
Definition: Solver.hpp:58
mango::Solver::finite_difference_Jacobian
void finite_difference_Jacobian(vector_function_type, int, const double *, double *, double *)
Definition: finite_difference_Jacobian.cpp:28
mango::vector_function_type
void(* vector_function_type)(int *N_parameters, const double *state_vector, int *N_terms, double *residuals, int *failed, mango::Problem *problem, void *user_data)
Format for the user-supplied subroutine that computes the residuals for a least-squares optimization ...
Definition: mango.hpp:439
mango::Solver::mpi_partition
MPI_Partition * mpi_partition
Definition: Solver.hpp:65
mango::Solver::evaluate_set_in_parallel
void evaluate_set_in_parallel(vector_function_type, int, int, double *, double *, bool *)
Definition: evaluate_set_in_parallel.cpp:28
mango.hpp
mango::MPI_Partition::get_rank_world
int get_rank_world()
Get the MPI rank of this processor in MANGO's world communicator.
Definition: mpi_partition.cpp:66
mango::Solver::N_parameters
int N_parameters
Definition: Solver.hpp:43
mango::MPI_Partition::get_proc0_world
bool get_proc0_world()
Determine whether this MPI processor has rank 0 in MANGO's world communicator.
Definition: mpi_partition.cpp:56