|
k-dot-p 0.1
|
00001 /* 00002 *Optimizes a material according to some number. 00003 00004 00005 Copyright (C) 2011 Joseph Pingenot 00006 00007 This program is free software: you can redistribute it and/or modify 00008 it under the terms of the GNU Affero General Public License as published by 00009 the Free Software Foundation, either version 3 of the License, or 00010 (at your option) any later version. 00011 00012 This program is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 GNU Affero General Public License for more details. 00016 00017 You should have received a copy of the GNU Affero General Public License 00018 along with this program. If not, see <http://www.gnu.org/licenses/>. 00019 00020 */ 00021 00022 #ifndef ___LIBKDOTP_DIGITAL_ALLOY_OPTIMIZER_MPI_1D_HPP___ 00023 #define ___LIBKDOTP_DIGITAL_ALLOY_OPTIMIZER_MPI_1D_HPP___ 00024 00025 #include <math.h> 00026 #include <mpi.h> 00027 #include <gsl/gsl_rng.h> 00028 #include <gsl/gsl_errno.h> 00029 #include <libmodelxx/material.h++> 00030 #include <libmodelxx/generic_structure.h++> 00031 00032 namespace kdotp { 00033 namespace libmetacalc { 00034 namespace optimizer { 00035 00036 class digital_alloy_optimizer_1d_mpi { 00037 public: 00038 typedef void (*set_point_callback)(unsigned X, const kdotp::libmodelxx::structure::material* mat, void* privdat); 00039 enum goal { 00040 MAXIMIZE, 00041 MAXIMIZE_ABS, 00042 MINIMIZE, 00043 MAKE_ZERO 00044 }; 00045 00046 /**constructor and initializer in one 00047 *\param marker_material material used to mark the region to be optimized 00048 *\param _mat1 material to be used if the block is zero 00049 *\param _mat0 material to be used if the block is one 00050 *\param s generic_structure to be optimized 00051 *\param _set_point callback to use when setting a point, or NULL if you don't want to do anything 00052 *\param _blocksize number of points per block 00053 *\param _goal the goal of the optimization 00054 *\param err GError information if something failed. Untouched if successful. 00055 *\param privdat private data for the set_points callback (used when initializing) 00056 *\param N_nodes number of nodes being executed on 00057 *\param node node number for this program. 00058 *\param rng_seed seed for the random number generator (default is 0, i.e. no seed) 00059 *\param assignements list of initial material assignments for each block (or for all blocks) 00060 *\param assignement_expr function to evaluate to get the initial material assignments. 00061 *\param global_symbols global symbol table 00062 *\param temperature if specified or non-zero, use simulated annealing at the specified temperature. If the temperature is never set, the steepest-descent-related Monte Carlo algorithm will be used. 00063 *\note Starting and stopping points are fetched from the generic structure. 00064 *\note that the assignments are done on the block level, 00065 *i.e. in blocks of points. This is to improve efficiency and 00066 *accuracy, since structures can only be grown so finely, but 00067 *having a grid spacing which is a multiple of the minimum 00068 *feature size is desirable for high-accuracy calculations. 00069 *\note that any incommensurate number of points will be split 00070 *amongst the points as evenly as possible. 00071 *\note that this modifies the generic_structure, removing the marker material and replacing it with materials from the optimizer state. 00072 *\note that this constructor calls _set_point, since it has to initialize the material. 00073 */ 00074 digital_alloy_optimizer_1d_mpi( 00075 kdotp::libmodelxx::structure::material* marker_material, 00076 kdotp::libmodelxx::structure::material* _mat1, 00077 kdotp::libmodelxx::structure::material* _mat0, 00078 kdotp::libmodelxx::structure::generic_structure* s, 00079 set_point_callback _set_point, 00080 unsigned _blocksize, 00081 enum goal _goal, 00082 GError** err, 00083 void* privdat, 00084 int _N_nodes, 00085 int _node, 00086 unsigned _N_blocks=0, 00087 long unsigned int rng_seed=0, 00088 GList* assignments=NULL, 00089 struct ::kdotp::libmodelxx::postprocessing::function_exp *assignment_expr=NULL, 00090 double temperature=0, 00091 GHashTable *global_symbols=NULL 00092 ); 00093 00094 /*The two materials we can be using at each site*/ 00095 kdotp::libmodelxx::structure::material* mat1; 00096 kdotp::libmodelxx::structure::material* mat0; 00097 /*This stores the current material assignments.*/ 00098 short unsigned *current_assignments; 00099 /*This stores the set of bits which state that the material at that location is known optimum for this particular point*/ 00100 short unsigned *material_is_optimum; 00101 /*Pointer to the generic structure we're to modify. 00102 *\note Please note that although it's perfectly doable to just set the genstruct 00103 * and then use it to create the Hamiltonian over and over and over again, that it's 00104 * much much more efficient to use the callback to also set the Hamiltonian directly. 00105 *\note To manipulate the Hamiltonian directly, you'll need to set the material yourself 00106 * for now. I may later add an interface through the generic hamltonian class to accomplish 00107 * this. 00108 */ 00109 kdotp::libmodelxx::structure::generic_structure* genstruct; 00110 /*The callback to use when setting a point*/ 00111 set_point_callback call_set_point_callback; 00112 /*Starting and ending points. Derived in the constructor from 00113 the generic_structure. These points are *in*clusive. */ 00114 unsigned start_X; 00115 /*This one is exclusive, i.e. this is the first point after the optimizer region.*/ 00116 unsigned stop_X; 00117 /*Size of a block, in points*/ 00118 unsigned blocksize; 00119 /*Stores the number of points in each block*/ 00120 unsigned* points_in_block; 00121 /*This stores the point that the start of the block corresponds to*/ 00122 unsigned* point_start; 00123 /**the number of blocks*/ 00124 unsigned N_blocks; 00125 /**Previous value we're to track, to compare how we're changing. 00126 *\note this is a *pointer*; if it's NULL, the previous calculation was the first one, so all of the blocks had been changed. 00127 */ 00128 double previous_value; 00129 /**Which block was last changed. 00130 *\note this is a *pointer*; if it's NULL, the previous calculation was the first one, so all of the blocks had been changed. 00131 *\note this is the block which was just tested 00132 */ 00133 unsigned* current_block; 00134 enum goal goal; 00135 /**The GSL random number generator information.*/ 00136 gsl_rng* rng; 00137 unsigned long int seed; 00138 unsigned N_nodes; 00139 unsigned node; 00140 /*Temperature for simulated annealing*/ 00141 double T; 00142 00143 /*array containing the first block assigned to each node. There are N_nodes+1 values (the last is 00144 the off the end of the array!*/ 00145 int* node_assignments; 00146 /*The results of this node's calculation*/ 00147 double* results; 00148 /*The results of modifying the requested sites. Only on the root node; there are N_blocks nodes. 00149 */ 00150 double* all_results; 00151 00152 inline void randomize_array(short unsigned* array, unsigned int N) { 00153 for(unsigned i=0; i<N; i++) { 00154 /**\note that gsl_rng_uniform returns [0,1), so 0.5 will be considered in the upper half, 00155 * so that both options will be equally probable 00156 *\note Upper half of [0,1) => 1; lower => 0. 00157 */ 00158 if(gsl_rng_uniform(rng) >= 0.5) { 00159 array[i]=1; 00160 }else{ 00161 array[i]=0; 00162 } 00163 } 00164 } 00165 inline bool is_improved(double value) { 00166 switch(goal) { 00167 case MAXIMIZE: 00168 return (value > previous_value); 00169 case MAXIMIZE_ABS: 00170 return (fabs(value) > fabs(previous_value)); 00171 case MINIMIZE: 00172 return (value < previous_value); 00173 case MAKE_ZERO: 00174 return (fabs(value) < fabs(previous_value)); 00175 } 00176 return false; 00177 } 00178 inline double dist_to_goal(double value) { 00179 switch(goal) { 00180 case MAXIMIZE: 00181 return value - previous_value; 00182 case MAXIMIZE_ABS: 00183 return fabs(value) - fabs(previous_value); 00184 case MINIMIZE: 00185 return previous_value - value; 00186 case MAKE_ZERO: 00187 return fabs(previous_value) - fabs(previous_value); 00188 } 00189 return false; 00190 } 00191 inline void toggle_block(unsigned block, void* privdat) { 00192 if(current_assignments[block] == 0) { 00193 current_assignments[block] = 1; 00194 }else{ 00195 current_assignments[block] = 0; 00196 } 00197 set_block(block, privdat); 00198 } 00199 inline void set_block(unsigned block, void* privdat) { 00200 ::kdotp::libmodelxx::grid::cartesian_grid< ::kdotp::libmodelxx::structure::material*, 1, 1>* matgrid 00201 = (::kdotp::libmodelxx::grid::cartesian_grid< ::kdotp::libmodelxx::structure::material*, 1, 1>*) 00202 (genstruct->material_grid); 00203 unsigned final_point = point_start[block]; 00204 for(unsigned pt=0; pt<points_in_block[block]; pt++) { 00205 matgrid->set_point(&final_point, (current_assignments[block]==0)?&mat0:&mat1); 00206 if(call_set_point_callback != NULL) 00207 call_set_point_callback(final_point, (current_assignments[block]==0)?mat0:mat1, privdat); 00208 final_point++; 00209 } 00210 } 00211 00212 inline void set_material_from_current_assignments(void* privdat) { 00213 for(unsigned block=0; block<N_blocks; block++) { 00214 set_block(block, privdat); 00215 } 00216 } 00217 /**Does the first step in the algorithm. 00218 *\param privdat pointer to data for the set_point callback. 00219 *\return 0 if we've not converged, or 1 if we've converged. 00220 */ 00221 int do_step(void* privdat); 00222 00223 /*This performs the calculation without stepping through manually. 00224 *At each step, get_value is called with privdat 00225 *\param get_value callback to use after setting the materials. 00226 *\param privdat private data for the callback 00227 *\return NULL if we converged; GError* if there was an error. 00228 *\note that if NaN is returned, we stop and return an error 00229 *\note this should eventually deal with GErrors for better error reporting. 00230 */ 00231 GError* do_optimization(double (*get_value)(void* privdat, unsigned assignment, long unsigned iteration), void* get_value_privdat, void* set_point_privdat, double anneal_start, double anneal_end, unsigned n_anneal_steps, unsigned n_anneal_cycles, unsigned num_calculations_per_annealing_step); 00232 /**Resets the state to get ready to do an optimization. 00233 *\note that this randomizes the current assignments array, so this is what 00234 * you'll want to call to start from another initial condition 00235 */ 00236 void initialize(void* privdat); 00237 /**Initializes to a known state. 00238 *\param state the state to initialize to (array of N_blocks short unsigned ints) 00239 */ 00240 void initialize(const short unsigned* state, void* privdat); 00241 /*this should be virtual if we had any real inheritance, but we don't*/ 00242 ~digital_alloy_optimizer_1d_mpi(); 00243 private: 00244 digital_alloy_optimizer_1d_mpi() {} 00245 bool operator==(const digital_alloy_optimizer_1d_mpi& o) {return false;} 00246 digital_alloy_optimizer_1d_mpi operator=(const digital_alloy_optimizer_1d_mpi& o) {return *this;} 00247 digital_alloy_optimizer_1d_mpi(const digital_alloy_optimizer_1d_mpi& o){} 00248 }; 00249 00250 00251 00252 } 00253 } 00254 } 00255 00256 00257 #endif