BELFEM 0.9.3
Berkeley Lab Finite Element Framework
Loading...
Searching...
No Matches
cl_Genome.hpp
Go to the documentation of this file.
1/*
2 * BELFEM -- The Berkeley Lab Finite Element Framework
3 * Copyright (c) 2026, The Regents of the University of California,
4 * through Lawrence Berkeley National Laboratory (subject to receipt of any required
5 * approvals from the U.S. Dept. of Energy). All rights reserved.
6 *
7 * Developers: Christian Messe, Gregory Giard
8 *
9 * See the top-level LICENSE file for the complete license and disclaimer.
10 */
11
12#ifndef BELFEM_CL_DNA_HPP
13#define BELFEM_CL_DNA_HPP
14
15#include <cmath>
16#include <algorithm>
17
18#include "typedefs.hpp"
19#include "constants.hpp"
20#include "cl_Vector.hpp"
21#include "cl_Bitset.hpp"
22#include "random.hpp"
23
24namespace belfem
25{
26 // B: number of bits per chromosome
27 // N: number of chromosomes
28 template < size_t B, size_t N >
29 class Genome
30 {
31 const Vector< real > & mMinVals ; //<-- no need to copy every time,
32 const Vector< real > & mMaxVals ; // they are the same for all genomes
33 const Bitset< N > & mTypes ; // false: linear, true : log scale
34 Bitset< N * B > mDNA ; // the dna string
35 real mFitness = BELFEM_REAL_MAX;
36
37 public:
38
39 Genome( const Vector< real > & aMinVals,
40 const Vector< real > & aMaxVals,
41 const Bitset< N > & aTypes ) :
42 mMinVals( aMinVals ),
43 mMaxVals( aMaxVals ),
44 mTypes( aTypes )
45 {
46
47 }
48
49 void
50 set_values( const Vector< real > & aValues )
51 {
52 mDNA.reset();
53 for( size_t i=0; i<N; ++i )
54 {
55 real tValue = aValues( i );
56 real tMin = mMinVals( i );
57 real tMax = mMaxVals( i );
58
59 if ( std::abs( tMin - tMax ) < BELFEM_EPSILON )
60 {
61 continue;
62 }
63
64 // check if we store in logarithmic scaling
65 if ( mTypes.test( i ) )
66 {
67 tValue = std::log( tValue );
68 tMin = std::log( tMin );
69 tMax = std::log( tMax );
70 }
71
72 // clamp value
73 tValue = std::max( std::min( tValue, tMax ), tMin );
74
75 // normalize to [0, 1]
76 tValue = ( tValue - tMin ) / ( tMax - tMin );
77
78 // scale to [0, 2^B - 1] and encode
79 encode( i, static_cast< uint >( std::round( tValue * ( ( 1u << B ) - 1 ) ) ) );
80 }
81 }
82
83 void
84 get_values( Vector< real > & aValues ) const
85 {
86 for( size_t i=0; i<N; ++i )
87 {
88 // scale to [tMin, tMax]
89 real tMin = mMinVals( i );
90 real tMax = mMaxVals( i );
91
92 if ( std::abs( tMin - tMax ) < BELFEM_EPSILON )
93 {
94 aValues( i ) = tMin ;
95 continue;
96 }
97
98 // decode uint from bitset
99 uint tBits = decode( i );
100
101 // normalize to [0, 1]
102 real tValue = real( tBits ) / real( ( 1u << B ) - 1 );
103
104
105
106 // check if we stored in logarithmic scaling
107 if ( mTypes.test( i ) )
108 {
109 tMin = std::log( tMin );
110 tMax = std::log( tMax );
111
112 tValue = std::exp( tMin + tValue * ( tMax - tMin ) );
113
114 }
115 else
116 {
117 tValue = tMin + tValue * ( tMax - tMin );
118 }
119
120 // log and exp do not round-trip exactly -- exp( log( 100 ) )
121 // overshoots by a few ulp -- so the top of the range decodes to
122 // marginally more than the caller's maximum. set_values() and
123 // randomize() clamp on the way in; clamping here as well makes
124 // the bounds an invariant the caller can rely on.
125 aValues( i ) = std::max( std::min( tValue, mMaxVals( i ) ),
126 mMinVals( i ) );
127 }
128 }
129
130 void
132 {
133 mDNA.reset();
134 for( size_t i=0; i<N; ++i )
135 {
136 real tMin = mMinVals( i );
137 real tMax = mMaxVals( i );
138
139 if ( std::abs( tMin - tMax ) < BELFEM_EPSILON )
140 {
141 // do nothing
142 continue;
143 }
144 // for log-scale params, work in log space
145 if ( mTypes.test( i ) )
146 {
147 tMin = std::log( tMin );
148 tMax = std::log( tMax );
149 }
150
151 // Gaussian distribution centered at midpoint
152 real tMean = 0.5 * ( tMin + tMax );
153 real tStdDev = ( tMax - tMin ) / 6.0; // ±3σ covers ~99.7% of range
154
155 // Box-Muller transform to generate Gaussian from uniform
156 real u1 = rand();
157 real u2 = rand();
158
159 // avoid log(0)
160 u1 = std::max( u1, BELFEM_EPSILON );
161
162 real tZ = std::sqrt( -2.0 * std::log( u1 ) ) * std::cos( 2.0 * constant::pi * u2 );
163
164 real tValue = tMean + tStdDev * tZ ;
165
166 // clamp to [tMin, tMax]
167 tValue = std::max( std::min( tValue, tMax ), tMin );
168
169 // normalize to [0, 1]
170 tValue = ( tValue - tMin ) / ( tMax - tMin );
171
172 // encode
173 encode( i, static_cast< uint >( std::round( tValue * ( ( 1u << B ) - 1 ) ) ) );
174 }
175 }
176
177 void
178 inherit( const Genome * aMom, const Genome * aDad )
179 {
180 // total number of bits in the genome
181 size_t tNumBits = N * B;
182
183 // random split point for crossover
184 size_t tSplit = size_t( rand() * real( tNumBits ) );
185
186 // random bit to mutate
187 size_t tMutate = size_t( rand() * real( tNumBits ) );
188
189 // start with a clean slate
190 mDNA.reset();
191
192 // inherit bits [0, tSplit) from mom
193 for( size_t k=0; k<tSplit; ++k )
194 {
195 if( aMom->mDNA.test( k ) )
196 {
197 mDNA.set( k );
198 }
199 }
200
201 // inherit bits [tSplit, tNumBits) from dad
202 for( size_t k=tSplit; k<tNumBits; ++k )
203 {
204 if( aDad->mDNA.test( k ) )
205 {
206 mDNA.set( k );
207 }
208 }
209
210 // mutate one random bit
211 mDNA.flip( tMutate );
212 }
213
214 real
215 fitness() const
216 {
217 return mFitness;
218 }
219
220 void
221 set_fitness( const real aFitness )
222 {
223 mFitness = aFitness;
224 }
225
226 void
228 {
229 mDNA.reset();
230 mFitness = BELFEM_REAL_MAX;
231 }
232
233 bool
234 is_alive() const
235 {
236 return mFitness != BELFEM_REAL_MAX;
237 }
238
239 private:
240
241 uint
242 decode( const size_t aIndex ) const
243 {
244
245
246 uint aValue = 0 ;
247 uint tExp = 1 ;
248 size_t tCount = aIndex * B ;
249 for( size_t i=0; i<B; ++i )
250 {
251 if ( mDNA.test( tCount++ ) )
252 {
253 aValue += tExp ;
254 }
255 tExp *= 2 ;
256 }
257 return aValue ;
258 }
259
260 void
261 encode( const size_t aIndex, uint aValue )
262 {
263 size_t tCount = aIndex * B;
264 for(size_t i=0; i<B; ++i)
265 {
266 if (aValue & 1) // bitwise test
267 {
268 mDNA.set(tCount++);
269 } else
270 {
271 mDNA.reset(tCount++);
272 }
273 aValue >>= 1; // shift right
274 }
275 }
276 };
277
278 template < size_t B, size_t N >
280 {
281 bool
282 operator()( const Genome< B, N > * aA, const Genome< B, N > * aB )
283 {
284 return aA->fitness() < aB->fitness();
285 }
286 };
287
288}
289#endif //BELFEM_CL_DNA_HPP
Compile-time fixed-size bitset.
Definition cl_Bitset.hpp:32
bool test(const index_t aIndex) const
test if a bit is set
Definition cl_Bitset.hpp:138
Definition cl_Genome.hpp:30
void kill()
Definition cl_Genome.hpp:227
void set_fitness(const real aFitness)
Definition cl_Genome.hpp:221
void randomize()
Definition cl_Genome.hpp:131
Genome(const Vector< real > &aMinVals, const Vector< real > &aMaxVals, const Bitset< N > &aTypes)
Definition cl_Genome.hpp:39
void set_values(const Vector< real > &aValues)
Definition cl_Genome.hpp:50
real fitness() const
Definition cl_Genome.hpp:215
void inherit(const Genome *aMom, const Genome *aDad)
Definition cl_Genome.hpp:178
void get_values(Vector< real > &aValues) const
Definition cl_Genome.hpp:84
bool is_alive() const
Definition cl_Genome.hpp:234
const real pi
circle number
Definition constants.hpp:41
USER GUIDES:
Definition cl_Capacitor.cpp:16
unsigned int uint
Definition typedefs.hpp:30
constexpr real BELFEM_EPSILON
Definition typedefs.hpp:89
double real
Definition typedefs.hpp:36
size_t size_t
Definition typedefs.hpp:25
real rand()
a random number between 0 and 1 must call seed first
Definition random.hpp:86
B
Definition test_twist_crosscheck.py:39
i
Definition main.py:57
Definition cl_Genome.hpp:280
bool operator()(const Genome< B, N > *aA, const Genome< B, N > *aB)
Definition cl_Genome.hpp:282
#define BELFEM_REAL_MAX
Definition typedefs.hpp:80