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