|
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 */ 00021 00022 #include <gsl/gsl_errno.h> 00023 #include <libmetacalc/digital_alloy_optimizer_1d.h++> 00024 #include <libmodelxx/cartesian_grid.h++> 00025 #include <libmodelxx/randomness.h++> 00026 #include <libkdotp/gcc_hints.h> 00027 00028 #define DIGITAL_ALLOY_OPTIMIZER_1D_GERROR_DOMAIN 73 00029 00030 #define EPSILON 1e-9 00031 00032 using namespace kdotp::libmodelxx::grid; 00033 using namespace kdotp::libmodelxx::structure; 00034 00035 namespace kdotp { 00036 namespace libmetacalc { 00037 namespace optimizer { 00038 00039 digital_alloy_optimizer_1d 00040 ::digital_alloy_optimizer_1d( 00041 kdotp::libmodelxx::structure::material* marker_material, 00042 kdotp::libmodelxx::structure::material* _mat1, 00043 kdotp::libmodelxx::structure::material* _mat0, 00044 kdotp::libmodelxx::structure::generic_structure* s, 00045 set_point_callback _set_point, 00046 unsigned _blocksize, 00047 enum goal _goal, 00048 GError** err, 00049 void* privdat, 00050 unsigned _N_blocks, 00051 long unsigned int rng_seed, 00052 GList* assignments, 00053 struct ::kdotp::libmodelxx::postprocessing::function_exp *assignment_expr, 00054 GHashTable *global_symbols 00055 ) 00056 : mat1(_mat1), 00057 mat0(_mat0), 00058 current_assignments(NULL), 00059 material_is_optimum(NULL), 00060 genstruct(s), 00061 call_set_point_callback(_set_point), 00062 blocksize(_blocksize), 00063 points_in_block(NULL), 00064 point_start(NULL), 00065 N_blocks(_N_blocks), 00066 previous_value(0), 00067 current_block(NULL), 00068 goal(_goal), 00069 seed(rng_seed) 00070 { 00071 cartesian_grid<material*, 1, 1>* matgrid = (cartesian_grid<material*, 1, 1>*)(genstruct->material_grid); 00072 /*First, we need to find the extents of the region.*/ 00073 bool started=false; 00074 /*Easy way to make this a GList: start off with list=NULL, create a new (start,end) struct 00075 * on start, save it to the list on end. leave the started variable as it is. 00076 */ 00077 for(unsigned X=0; X<matgrid->L[0]; X++) { 00078 if((**(matgrid->get_point_r(&X))) == *marker_material) { 00079 if(!started) { 00080 start_X = X; 00081 started=1; 00082 } 00083 }else if(started) { 00084 /*We've stopped*/ 00085 stop_X = X; 00086 started=0; 00087 } 00088 } 00089 if(started) { 00090 /*We started but hit the end without switching out of the material. Odd, but solvable.*/ 00091 stop_X = matgrid->L[0]; 00092 } 00093 /*We could probably do this with rounding, but this only happens once and works.*/ 00094 if(_N_blocks > 0) { 00095 N_blocks = (unsigned)_N_blocks; 00096 blocksize = ((stop_X-start_X) - (stop_X-start_X)%N_blocks)/N_blocks; 00097 }else{ 00098 N_blocks = ((stop_X-start_X) - (stop_X-start_X)%blocksize)/blocksize; 00099 } 00100 /*Allocate arrays*/ 00101 points_in_block = new unsigned[N_blocks]; 00102 point_start = new unsigned[N_blocks]; 00103 current_assignments = new short unsigned[N_blocks]; 00104 /*Previous assignments is NULL in order to know that we have no previous assignment or value.*/ 00105 material_is_optimum = new short unsigned[N_blocks]; 00106 /*Map the points to the blocks*/ 00107 for(unsigned i=0; i<N_blocks; i++) { 00108 points_in_block[i] = blocksize; 00109 } 00110 unsigned points_remaining = (stop_X - start_X)%blocksize; 00111 if(points_remaining != 0) { 00112 fprintf(stderr, "N_blocks=%u\n", N_blocks); 00113 unsigned pos=points_remaining/N_blocks; 00114 for(unsigned pt=0; pt<points_remaining; pt++) { 00115 points_in_block[pos]++; 00116 pos += points_remaining/N_blocks; 00117 if(pos >= N_blocks) pos = pos%N_blocks; 00118 } 00119 } 00120 /*Put in the offsets.*/ 00121 unsigned final_point = start_X; 00122 for(unsigned block=0; block<N_blocks; block++) { 00123 point_start[block] = final_point; 00124 final_point += points_in_block[block]; 00125 } 00126 00127 /*allocate and then seed the random number generator*/ 00128 GError *e = NULL; 00129 gsl_rng_env_setup(); 00130 rng = gsl_rng_alloc(gsl_rng_default); 00131 if(rng == NULL) { 00132 *err = g_error_new(DIGITAL_ALLOY_OPTIMIZER_1D_GERROR_DOMAIN, 0, "kdotp::libkdotp::optimizer::digital_alloy_optimizer_1d.c::digital_alloy_optimizer_1d(constructor): Failed to set up GSL random number generator."); 00133 return; 00134 } 00135 /*Set the seed randomly if it's not already set*/ 00136 if(rng_seed == 0) { 00137 /*A seed value of 0 is special to GSL, so don't allow a seed of 0. Loop until it's not, or an error has occurred*/ 00138 seed=0; 00139 while((seed == 0) && (e == NULL)) { 00140 seed = kdotp::libmodelxx::randomness::get_urandom_unsigned_long_int(&e); 00141 } 00142 if(e != NULL) { 00143 g_propagate_prefixed_error(err, e, "kdotp::libkdotp::optimizer::digital_alloy_optimizer_1d.c::digital_alloy_optimizer_1d(constructor): Failed to get random seed:\n\t"); 00144 return; 00145 } 00146 fprintf(stderr, "rng_seed was zero, so got new one (seed=%lu rng_seed=%lu)\n", seed, rng_seed); 00147 }else{ 00148 fprintf(stderr, "rng_seed was non-zero, so keeping it (seed=%lu rng_seed=%lu)\n", seed, rng_seed); 00149 } 00150 fprintf(stderr, "\n\nseed is %lu\n\n", seed); 00151 /*We now seed the rng*/ 00152 gsl_rng_set(rng, seed); 00153 /*Begin initialization.*/ 00154 if((assignments != NULL) || (assignment_expr != NULL)) { 00155 /*NOTE that assignments takes precedence over assignment_expr*/ 00156 short unsigned* state = new short unsigned[N_blocks]; 00157 if(assignments != NULL) { 00158 if(assignments->next == NULL) { 00159 /*Assign each block based on the single user-supplied value.*/ 00160 if(*(double*)(assignments->data) < 0.5) { 00161 for(unsigned i=0; i<N_blocks; i++) state[i] = 0; 00162 }else{ 00163 for(unsigned i=0; i<N_blocks; i++) state[i] = 1; 00164 } 00165 }else{ 00166 /*Assign each block based on the user's specification.*/ 00167 GList* ll=assignments; 00168 for(unsigned i=0; i<N_blocks; i++) { 00169 state[i] = (*(double*)(ll->data) < 0.5)?0:1; 00170 ll = ll->next; 00171 if(ll == NULL) { 00172 *err = g_error_new(DIGITAL_ALLOY_OPTIMIZER_1D_GERROR_DOMAIN, 5, "kdotp::libkdotp::optimizer::digital_alloy_optimizer_1d.c::digital_alloy_optimizer_1d(constructor): ERROR while assigning materials according to user input: there were not enough values for each block (Got %u values but had %u blocks!\n", i+1, N_blocks); 00173 return; 00174 } 00175 } 00176 } 00177 }else{ 00178 /*Evaluate the expression at each point in the blocks.*/ 00179 struct ::kdotp::libmodelxx::postprocessing::symbol_table_entry_const start_x = {::kdotp::libmodelxx::postprocessing::CONSTANT_SYMBOL, ::kdotp::libmodelxx::postprocessing::SCALAR, NULL}; 00180 struct ::kdotp::libmodelxx::postprocessing::symbol_table_entry_const mid_x = {::kdotp::libmodelxx::postprocessing::CONSTANT_SYMBOL, ::kdotp::libmodelxx::postprocessing::SCALAR, NULL}; 00181 struct ::kdotp::libmodelxx::postprocessing::symbol_table_entry_const end_x = {::kdotp::libmodelxx::postprocessing::CONSTANT_SYMBOL, ::kdotp::libmodelxx::postprocessing::SCALAR, NULL}; 00182 struct ::kdotp::libmodelxx::postprocessing::symbol_table_entry_const start_X = {::kdotp::libmodelxx::postprocessing::CONSTANT_SYMBOL, ::kdotp::libmodelxx::postprocessing::SCALAR, NULL}; 00183 struct ::kdotp::libmodelxx::postprocessing::symbol_table_entry_const mid_X = {::kdotp::libmodelxx::postprocessing::CONSTANT_SYMBOL, ::kdotp::libmodelxx::postprocessing::SCALAR, NULL}; 00184 struct ::kdotp::libmodelxx::postprocessing::symbol_table_entry_const end_X = {::kdotp::libmodelxx::postprocessing::CONSTANT_SYMBOL, ::kdotp::libmodelxx::postprocessing::SCALAR, NULL}; 00185 /**TODO: need to finish symbol assignments, make the local symbol table, and then do the loop.*/ 00186 } 00187 initialize(state, privdat); 00188 }else{ 00189 initialize(privdat); 00190 } 00191 /*At this point, we should be set up. 00192 *The user should perform their first calculation, and call us back using one of the do_*_step funx.*/ 00193 } 00194 00195 void digital_alloy_optimizer_1d::initialize(void* privdat) { 00196 if(current_block != NULL) { 00197 delete current_block; 00198 current_block = NULL; 00199 } 00200 reset_optimum_array(); 00201 randomize_array(current_assignments, N_blocks); 00202 set_material_from_current_assignments(privdat); 00203 } 00204 00205 void digital_alloy_optimizer_1d::initialize(const short unsigned* state, void* privdat) { 00206 if(current_block != NULL) { 00207 delete current_block; 00208 current_block = NULL; 00209 } 00210 reset_optimum_array(); 00211 for(unsigned i=0; i<N_blocks; i++) current_assignments[i] = state[i]; 00212 set_material_from_current_assignments(privdat); 00213 } 00214 00215 digital_alloy_optimizer_1d::~digital_alloy_optimizer_1d() { 00216 if(mat1 != NULL) delete mat1; 00217 if(mat0 != NULL) delete mat0; 00218 if(current_assignments != NULL) delete[] current_assignments; 00219 if(material_is_optimum != NULL) delete[] material_is_optimum; 00220 if(points_in_block != NULL) delete[] points_in_block; 00221 if(current_block != NULL) delete current_block; 00222 } 00223 00224 /**Does the first step in the algorithm. 00225 *\param value the value that we're tracking 00226 *\param privdat pointer to data for the set_point callback. 00227 *\return 0 if we've not converged, or 1 if we've converged. 00228 */ 00229 int digital_alloy_optimizer_1d::do_first_step(double value, void* privdat) { 00230 /*There's no way we can yet be converged. We don't even know if this 00231 * point is optimal. This is our first point.*/ 00232 previous_value=value; 00233 current_block = new unsigned; 00234 *current_block = get_random_unoptimized_block(); 00235 toggle_block(*current_block, privdat); 00236 return 0; 00237 } 00238 /**Does the later steps in the algorithm 00239 *\param value the value that we're tracking 00240 *\param privdat pointer to data for the set_point callback. 00241 *\return 0 if we've not converged, or 1 if we've converged. 00242 */ 00243 int digital_alloy_optimizer_1d::do_subsequent_step(double value, void* privdat) { 00244 if(is_improved(value)) { 00245 /*This is an improved point!*/ 00246 /*We don't know right now if any other points are optimized.*/ 00247 reset_optimum_array(); 00248 /**Set the previous value. 00249 *\note if structure is now optimum, we have the final value in previous_value. 00250 */ 00251 previous_value = value; 00252 fprintf(stderr, "RESET ARRAY: previous_value=%g (value=%g)\n", previous_value, value); 00253 }else{ 00254 /*Choosing this point worsened the structure. Go back, set this as optimized, and choose a new point.*/ 00255 toggle_block(*current_block, privdat); 00256 fprintf(stderr, "REVERT BLOCK %u: previous_value=%g (value=%g)\n", *current_block, previous_value, value); 00257 /*Note that we don't set the previous value to this current value; 00258 *it's not an improvement! 00259 */ 00260 } 00261 /*We know that the block that just got changed is optimized.*/ 00262 set_block_as_optimum(*current_block); 00263 /*Check to see if they're all optimized.*/ 00264 if(N_optimum == N_blocks) { 00265 fprintf(stderr, "ALPHA IS LOCAL OPTIMUM: N_optimum=%u N_blocks=%u\n", N_optimum, N_blocks); 00266 return 1; 00267 }else{ 00268 fprintf(stderr, "ALPHA IS (NOT) LOCAL OPTIMUM: N_optimum=%u N_blocks=%u\n", N_optimum, N_blocks); 00269 } 00270 /*Get the randomly-selected new block.*/ 00271 *current_block = get_random_unoptimized_block(); 00272 /*Set the block.*/ 00273 toggle_block(*current_block, privdat); 00274 return 0; 00275 } 00276 /**Does any step in the algorithm, but checks to see if we've taken the first step yet 00277 *\param value the value that we're tracking 00278 *\param privdat pointer to data for the set_point callback. 00279 *\return 0 if we've not converged, or 1 if we've converged. 00280 */ 00281 int digital_alloy_optimizer_1d::do_generic_step(double value, void* privdat) { 00282 if(current_block == NULL) { 00283 return do_first_step(value, privdat); 00284 } 00285 return do_subsequent_step(value, privdat); 00286 } 00287 00288 /*This performs the calculation without stepping through manually. 00289 *At each step, get_value is called with privdat 00290 *\param get_value callback to use after setting the materials. 00291 *\param privdat private data for the callback 00292 *\return NULL if we converged; GError* if there was an error. 00293 *\note that if NaN is returned, we stop and return an error 00294 *\note that initialize() should have been called before calling this function, either directly or implicitly in the constructor. 00295 */ 00296 GError* digital_alloy_optimizer_1d::do_optimization(double (*get_value)(void* privdat, unsigned assignment, long unsigned iteration), void* get_value_privdat, void* set_point_privdat) { 00297 long unsigned iteration=0; 00298 double value = get_value(get_value_privdat, 0, iteration); 00299 if(unlikely(isnan(value))) { 00300 return g_error_new(DIGITAL_ALLOY_OPTIMIZER_1D_GERROR_DOMAIN, 101, "kdotp::libkdotp::optimizer::digital_alloy_optimizer_1d.c::do_optimization: Got NaN (%g) from get_value callback in the %lu%s iteration. Iteration has been halted.\n", value, iteration, (iteration%10==1&&iteration!=11)?"st":(iteration%10==2&&iteration!=12)?"nd":(iteration%10==3&&iteration!=13)?"rd":"th"); 00301 }else if(unlikely(isinf(value))) { 00302 return g_error_new(DIGITAL_ALLOY_OPTIMIZER_1D_GERROR_DOMAIN, 101, "kdotp::libkdotp::optimizer::digital_alloy_optimizer_1d.c::do_optimization: Got inf (%g) from get_value callback in the %lu%s iteration. Iteration has been halted.\n", value, iteration, (iteration%10==1&&iteration!=11)?"st":(iteration%10==2&&iteration!=12)?"nd":(iteration%10==3&&iteration!=13)?"rd":"th"); 00303 } 00304 do_first_step(value, set_point_privdat); 00305 do { 00306 value = get_value(get_value_privdat, 0, ++iteration); 00307 if(unlikely(isnan(value))) { 00308 return g_error_new(DIGITAL_ALLOY_OPTIMIZER_1D_GERROR_DOMAIN, 101, "kdotp::libkdotp::optimizer::digital_alloy_optimizer_1d.c::do_optimization: Got NaN (%g) from get_value callback in the %lu%s iteration. Iteration has been halted.\n", value, iteration, (iteration%10==1&&iteration!=11)?"st":(iteration%10==2&&iteration!=12)?"nd":(iteration%10==3&&iteration!=13)?"rd":"th"); 00309 }else if(unlikely(isinf(value))) { 00310 return g_error_new(DIGITAL_ALLOY_OPTIMIZER_1D_GERROR_DOMAIN, 101, "kdotp::libkdotp::optimizer::digital_alloy_optimizer_1d.c::do_optimization: Got inf (%g) from get_value callback in the %lu%s iteration. Iteration has been halted.\n", value, iteration, (iteration%10==1&&iteration!=11)?"st":(iteration%10==2&&iteration!=12)?"nd":(iteration%10==3&&iteration!=13)?"rd":"th"); 00311 } 00312 }while(!(do_subsequent_step(value, set_point_privdat))); 00313 return NULL; 00314 } 00315 00316 } 00317 } 00318 }