k-dot-p 0.1

digital_alloy_optimizer_1d.c++

Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines