k-dot-p 0.1

continuous_alloy_optimizer_1d.h++

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 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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines