|
k-dot-p 0.1
|
00001 /* 00002 *Optimizes a material according to some number. 00003 00004 00005 Copyright (C) 2010 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 NOTES: 00021 * "block" is a set of points which are assigned a material and treated as a unit. 00022 * "signed" vs "unsigned" blocks: because we can make a step in either a positive 00023 or negative direction, we have two options for every block. Thus, we have 00024 2*N_blocks options to check, and the optimized-block tracking array must therefore 00025 be 2*N_blocks. Ultimately, however, both positive and negative steps map to the same 00026 block for material assignments, which is the "unsigned" block. 00027 */ 00028 00029 #ifndef ___LIBKDOTP_CONTINUOUS_ALLOY_OPTIMIZER_1D_HPP___ 00030 #define ___LIBKDOTP_CONTINUOUS_ALLOY_OPTIMIZER_1D_HPP___ 00031 00032 #include <math.h> 00033 #include <gsl/gsl_rng.h> 00034 #include <gsl/gsl_errno.h> 00035 #include <libmodelxx/material.h++> 00036 #include <libmodelxx/generic_structure.h++> 00037 #include <libmodelxx/function_specification.h++> 00038 00039 namespace kdotp { 00040 namespace libmetacalc { 00041 namespace optimizer { 00042 00043 class continuous_alloy_optimizer_1d { 00044 public: 00045 typedef void (*set_point_callback)(unsigned X, const kdotp::libmodelxx::structure::material* mat, void* privdat); 00046 enum goal { 00047 MAXIMIZE, 00048 MAXIMIZE_ABS, 00049 MINIMIZE, 00050 MAKE_ZERO 00051 }; 00052 00053 /**constructor and initializer in one 00054 *\param marker_material material used to mark the region to be optimized 00055 *\param a material to be used if the block is zero 00056 *\param b material to be used if the block is one 00057 *\param s generic_structure to be optimized 00058 *\param set_point callback to use when setting a point, or NULL if you don't want to do anything 00059 *\param blocksize number of points per block (ignored if _N_blocks > 0) 00060 *\param err GError information if something failed. Untouched if successful. 00061 *\param minx minimum x to be allowed 00062 *\param maxx maximum x to be allowed 00063 *\param _N_blocks Number of blocks to allocate, or <=0 to use the specified blocksize. 00064 *\param assignements list of initial material assignments for each block (or for all blocks) 00065 *\param assignement_expr function to evaluate to get the initial material assignments. 00066 *\note Starting and stopping points are fetched from the generic structure. 00067 *\note that the assignments are done on the block level, 00068 *i.e. in blocks of points. This is to improve efficiency and 00069 *accuracy, since structures can only be grown so finely, but 00070 *having a grid spacing which is a multiple of the minimum 00071 *feature size is desirable for high-accuracy calculations. 00072 *\note that any incommensurate number of points will be split 00073 *amongst the points as evenly as possible. 00074 *\note that this modifies the generic_structure, removing the marker material and replacing it with materials from the optimizer state. 00075 *\note that this constructor calls _set_point, since it has to initialize the material. 00076 */ 00077 continuous_alloy_optimizer_1d( 00078 kdotp::libmodelxx::structure::material* marker_material, 00079 const char* _mat1, 00080 const char* _mat0, 00081 kdotp::libmodelxx::structure::generic_structure* s, 00082 set_point_callback _set_point, 00083 unsigned _blocksize, 00084 enum goal _goal, 00085 GError** err, 00086 void* privdat, 00087 struct ::kdotp::libmodelxx::structure::matdb *_mdb, 00088 double _minx=0.0, 00089 double _maxx=1.0, 00090 long unsigned int rng_seed=0, 00091 double starting_stepsize=0.1, 00092 unsigned _N_blocks=0, 00093 GList* assignments=NULL, 00094 struct ::kdotp::libmodelxx::postprocessing::function_exp *assignment_expr=NULL, 00095 GHashTable *global_symbols=NULL 00096 ); 00097 00098 /*The two materials we can be using at each site*/ 00099 char* mat1; 00100 char* mat0; 00101 /*This stores the current material assignments.*/ 00102 double *current_assignments; 00103 /*This stores the set of bits which state that the material at that location is known 00104 *optimum for this particular point 00105 *\note this is 2*N_blocks: one for each way we could step (+stepsize, -stepsize) 00106 *\note array is ordered as this: +block0, +block1, +block2, ... , -block0, -block1, -block2 ... 00107 */ 00108 short unsigned *material_is_optimum; 00109 /*Pointer to the generic structure we're to modify. 00110 *\note Please note that although it's perfectly doable to just set the genstruct 00111 * and then use it to create the Hamiltonian over and over and over again, that it's 00112 * much much more efficient to use the callback to also set the Hamiltonian directly. 00113 *\note To manipulate the Hamiltonian directly, you'll need to set the material yourself 00114 * for now. I may later add an interface through the generic hamltonian class to accomplish 00115 * this. 00116 */ 00117 kdotp::libmodelxx::structure::generic_structure* genstruct; 00118 /*The callback to use when setting a point*/ 00119 set_point_callback call_set_point_callback; 00120 /*Starting and ending points. Derived in the constructor from 00121 the generic_structure. These points are *in*clusive. */ 00122 unsigned start_X; 00123 /*This one is exclusive, i.e. this is the first point after the optimizer region.*/ 00124 unsigned stop_X; 00125 /*Size of a block, in points*/ 00126 unsigned blocksize; 00127 /*Stores the number of points in each block*/ 00128 unsigned* points_in_block; 00129 /*This stores the point that the start of the block corresponds to*/ 00130 unsigned* point_start; 00131 /**the number of blocks*/ 00132 unsigned N_blocks; 00133 /**This could be gleaned by counting material_is_optimum, but is faster.*/ 00134 unsigned N_optimum; 00135 /**Previous value we're to track, to compare how we're changing. 00136 *\note this is a *pointer*; if it's NULL, the previous calculation was the first one, so all of the blocks had been changed. 00137 */ 00138 double previous_value; 00139 /**Which block was last changed. 00140 *\note this is a *pointer*; if it's NULL, the previous calculation was the first one, so all of the blocks had been changed. 00141 *\note this is the block which was just tested 00142 *\note that this is in the range [0, 2*N_blocks-1] to preserve the sign information. 00143 */ 00144 unsigned* current_block; 00145 enum goal goal; 00146 /**The GSL random number generator information.*/ 00147 gsl_rng* rng; 00148 unsigned long int seed; 00149 /**This is our current stepsize.*/ 00150 double stepsize; 00151 /**previous block x, so we can reset the block to its previous state*/ 00152 double prev_x; 00153 struct kdotp::libmodelxx::structure::matdb *mdb; 00154 /*Min and max x values*/ 00155 double minx; 00156 double maxx; 00157 00158 /*Internal methods*/ 00159 inline void reset_optimum_array() { 00160 for(unsigned i=0; i<2*N_blocks; i++) material_is_optimum[i]=0; 00161 N_optimum=0; 00162 } 00163 inline void randomize_array(double* array, unsigned int N) { 00164 for(unsigned i=0; i<N; i++) { 00165 /**\note that gsl_rng_uniform returns [0,1) but that's good enough. 00166 */ 00167 array[i]=minx+gsl_rng_uniform(rng)*(maxx-minx); 00168 } 00169 } 00170 inline bool is_improved(double value) { 00171 switch(goal) { 00172 case MAXIMIZE: 00173 return (value > previous_value); 00174 break; 00175 case MAXIMIZE_ABS: 00176 fprintf(stderr, "maximize_abs: value=%g previous_value=%g\n", value, previous_value); 00177 return (fabs(value) > fabs(previous_value)); 00178 break; 00179 case MINIMIZE: 00180 return (value < previous_value); 00181 break; 00182 case MAKE_ZERO: 00183 return (fabs(value) < fabs(previous_value)); 00184 break; 00185 } 00186 return false; 00187 } 00188 /**gets a random unoptimized block. 00189 *\param sign pointer to a double to store either + or -1, depending on the block. 00190 *\note that double pointed to by sign will be either +1 or -1; mul by stepsize to get the amount. 00191 *\return the block which is returned is [0, 2*N_blocks-1] 00192 */ 00193 inline unsigned get_random_unoptimized_block(double* sign) { 00194 unsigned check = (unsigned)floor(gsl_rng_uniform(rng)*(2*N_blocks - N_optimum-1)); 00195 unsigned j=0; 00196 /*we need to find the check-th un-optimized block. 00197 *algorithm: walk through the list incrementing j when we have an unoptimized block 00198 * return the block where j becomes check. 00199 */ 00200 /**\note we first walk through the first half, then the second.*/ 00201 *sign = 1.0; 00202 for(unsigned block=0; block<N_blocks; block++) { 00203 if(!material_is_optimum[block]) { 00204 if(j++ == check) { 00205 return block; 00206 } 00207 } 00208 } 00209 /**we're now in the negative blocks.*/ 00210 *sign = -1.0; 00211 for(unsigned block=0; block<N_blocks; block++) { 00212 if(!material_is_optimum[block]) { 00213 if(j++ == check) { 00214 return N_blocks+block; 00215 } 00216 } 00217 } 00218 return 2*N_blocks; 00219 } 00220 /**Steps a block by the specified step 00221 *\param signed block 00222 *\param privdat private data for the set_point callback. 00223 *\return true if stepping succeeded; false if stepping failed. 00224 */ 00225 inline bool step_block(unsigned block, void* privdat) { 00226 double sign = -1; 00227 if(block < N_blocks) sign=1; 00228 if(current_assignments[block%N_blocks]+sign*stepsize > maxx) return false; 00229 if(current_assignments[block%N_blocks]+sign*stepsize < minx) return false; 00230 fprintf(stderr, "STEPPING: step_block: sign=%g stepsize=%g block[%u->%u]=%g->", sign, stepsize, block, block%N_blocks, current_assignments[block%N_blocks]); 00231 current_assignments[block%N_blocks] += sign*stepsize; 00232 fprintf(stderr, "%g\n", current_assignments[block%N_blocks]); 00233 set_block(block%N_blocks, privdat); 00234 return true; 00235 } 00236 /**Gets the x value of a block 00237 *\param signed block 00238 */ 00239 inline double get_x(unsigned block) { 00240 return current_assignments[block%N_blocks]; 00241 } 00242 /**Sets a block to the specified x value 00243 *\param signed block 00244 */ 00245 inline void set_block(unsigned block, void* privdat, double x) { 00246 current_assignments[block%N_blocks] = x; 00247 set_block(block%N_blocks, privdat); 00248 } 00249 /**\note block is an unsigned block*/ 00250 inline void set_block(unsigned block, void* privdat) { 00251 ::kdotp::libmodelxx::grid::cartesian_grid< ::kdotp::libmodelxx::structure::material*, 1, 1>* matgrid 00252 = (::kdotp::libmodelxx::grid::cartesian_grid< ::kdotp::libmodelxx::structure::material*, 1, 1>*) 00253 (genstruct->material_grid); 00254 unsigned final_point = point_start[block]; 00255 for(unsigned pt=0; pt<points_in_block[block]; pt++) { 00256 ::kdotp::libmodelxx::structure::material** m; 00257 if((m=matgrid->get_point_r(&final_point)) != NULL) delete *m; 00258 ::kdotp::libmodelxx::structure::material* mat = new ::kdotp::libmodelxx::structure::material(mdb, mat1, mat0, current_assignments[block]); 00259 matgrid->set_point(&final_point, mat); 00260 if(call_set_point_callback != NULL) 00261 call_set_point_callback(final_point, mat, privdat); 00262 final_point++; 00263 } 00264 } 00265 inline void set_block_as_optimum(unsigned block) { 00266 material_is_optimum[block]=1; 00267 N_optimum++; 00268 } 00269 00270 inline void set_material_from_current_assignments(void* privdat) { 00271 for(unsigned block=0; block<N_blocks; block++) { 00272 set_block(block, privdat); 00273 } 00274 } 00275 /**Does the first step in the algorithm. 00276 *\param value the value that we're tracking 00277 *\param privdat pointer to data for the set_point callback. 00278 *\return 0 if we've not converged, or 1 if we've converged. 00279 */ 00280 int do_first_step(double value, void* privdat); 00281 /**Does the later steps in the algorithm 00282 *\param value the value that we're tracking 00283 *\param privdat pointer to data for the set_point callback. 00284 *\return 0 if we've not converged, or 1 if we've converged. 00285 */ 00286 int do_second_step(double value, void* privdat); 00287 /**Does the later steps in the algorithm 00288 *\param value the value that we're tracking 00289 *\param privdat pointer to data for the set_point callback. 00290 *\return 0 if we've not converged, or 1 if we've converged. 00291 */ 00292 int do_subsequent_step(double value, void* privdat); 00293 /**Does any step in the algorithm, but checks to see if we've taken the first step yet. 00294 *\param value the value that we're tracking 00295 *\param privdat pointer to data for the set_point callback. 00296 *\return 0 if we've not converged, or 1 if we've converged. 00297 */ 00298 int do_generic_step(double value, void* privdat); 00299 00300 /*This performs the calculation without stepping through manually. 00301 *At each step, get_value is called with privdat 00302 *\param get_value callback to use after setting the materials. 00303 *\param privdat private data for the callback 00304 *\return NULL if we converged; GError* if there was an error. 00305 *\note that if NaN is returned, we stop and return an error 00306 *\note this should eventually deal with GErrors for better error reporting. 00307 */ 00308 GError* do_optimization(double (*get_value)(void* privdat, unsigned assignment, long unsigned iteration), void* get_value_privdat, void* set_point_privdat, double step_fraction=0.33, double min_stepsize=0.001); 00309 /**Resets the state to get ready to do an optimization. 00310 *\note that this randomizes the current assignments array, so this is what 00311 * you'll want to call to start from another initial condition 00312 */ 00313 void initialize(void* privdat); 00314 /**Initializes to a known state. 00315 *\param state the state to initialize to (array of N_blocks short unsigned ints) 00316 */ 00317 void initialize(const short unsigned* state, void* privdat); 00318 /*this should be virtual if we had any real inheritance, but we don't*/ 00319 ~continuous_alloy_optimizer_1d(); 00320 private: 00321 continuous_alloy_optimizer_1d() {} 00322 bool operator==(const continuous_alloy_optimizer_1d& o) {return false;} 00323 continuous_alloy_optimizer_1d operator=(const continuous_alloy_optimizer_1d& o) {return *this;} 00324 continuous_alloy_optimizer_1d(const continuous_alloy_optimizer_1d& o){} 00325 }; 00326 00327 00328 00329 } 00330 } 00331 } 00332 00333 00334 #endif