66# A basic chicken swarm optimization class.
77#
88# Author(s): Lauren Linkous, Jonathan Lundquist
9- # Last update:March 13 , 2025
9+ # Last update:june 19 , 2025
1010##--------------------------------------------------------------------\
1111
1212
@@ -22,7 +22,8 @@ class swarm:
2222# func, func,
2323# dataFrame,
2424# class obj,
25- # bool, [int, int, ...])
25+ # bool, [int, int, ...],
26+ # int)
2627#
2728# opt_df contains class-specific tuning parameters
2829# boundary: int. 1 = random, 2 = reflecting, 3 = absorbing, 4 = invisible
@@ -37,11 +38,17 @@ def __init__(self, lbound, ubound, targets,E_TOL, maxit,
3738obj_func ,constr_func ,
3839opt_df ,
3940parent = None ,
40- evaluate_threshold = False ,obj_threshold = None ):
41+ evaluate_threshold = False ,obj_threshold = None ,
42+ decimal_limit = 4 ):
4143
4244# Optional parent class func call to write out values that trigger constraint issues
4345self .parent = parent
4446
47+
48+ self .number_decimals = int (decimal_limit )# limit the number of decimals
49+ # used in cases where real life has limitations on resolution
50+
51+
4552#evaluation method for targets
4653# True: Evaluate as true targets
4754# False: Evaluate as thesholds based on information in obj_threshold
@@ -159,10 +166,14 @@ def __init__(self, lbound, ubound, targets,E_TOL, maxit,
159166self .chicken_info = np .array ([0 ,0 ,- 1 ])
160167
161168#randomly initialize the positions
162- self .M = np .vstack (np .multiply (self .rng .random ((np .max ([heightl ,
163- widthl ]),1 )),
164- variation )+
165- lbound )
169+ # self.M = np.vstack(np.multiply(self.rng.random((np.max([heightl,
170+ # widthl]),1)),
171+ # variation) +
172+ # lbound)
173+
174+ self .M = np .round (np .array (np .multiply (self .rng .random ((1 ,np .max ([heightl ,widthl ]))),variation )+ lbound ),self .number_decimals )
175+
176+
166177
167178
168179if NO_OF_PARTICLES > 1 :
@@ -187,10 +198,10 @@ def __init__(self, lbound, ubound, targets,E_TOL, maxit,
187198for i in range (2 ,int (NO_OF_PARTICLES )+ 1 ):
188199# set initial location
189200self .M = \
190- np .vstack ([self .M ,
201+ np .round ( np . vstack ([self .M ,
191202np .multiply (self .rng .random ((1 ,np .max ([heightl ,widthl ]))),
192203variation )
193- + lbound ])
204+ + lbound ]), self . number_decimals )
194205if classList [i - 1 ]== 0 :#rooster
195206# assign to the next group (i-1), and done.
196207self .chicken_info = \
@@ -382,7 +393,7 @@ def move_rooster(self, particle):
382393
383394
384395#update new location based on random()
385- self .M [particle ]= self .M [particle ]* (1 + self .rng .normal (0 ,sig_squared ))
396+ self .M [particle ]= np . round ( self .M [particle ]* (1 + self .rng .normal (0 ,sig_squared )), self . number_decimals )
386397
387398
388399def move_hen (self ,particle ):
@@ -426,9 +437,9 @@ def move_hen(self, particle):
426437# these clipped bounds are effectively are zero and inf
427438# term_1 = S1*self.rng.uniform(0,1)*(rooster_loc-self.M[particle])
428439# still dealing with overflow issues. apply cap to S1
429- S1 = np .clip (S1 ,- 10e50 , 10e50 )
440+ S1 = np .clip (S1 ,- 10e30 , 10e30 )
430441
431- clipped_term1 = np .clip ((S1 * self .rng .uniform (0 ,1 )* (rooster_loc - self .M [particle ])),- 10e50 ,10e10 )
442+ clipped_term1 = np .clip ((S1 * self .rng .uniform (0 ,1 )* (rooster_loc - self .M [particle ])),- 10e30 ,10e10 )
432443term_1 = clipped_term1
433444
434445#S2 = np.exp(float(fitness_random_chicken-fitness_this_chicken))
@@ -443,13 +454,13 @@ def move_hen(self, particle):
443454#term_2 = S2*self.rng.uniform(0,1)*(random_chicken_loc-self.M[particle])
444455# This still causes overflow:
445456# clipped_term2 = np.clip((S2*self.rng.uniform(0,1)*(random_chicken_loc-self.M[particle])), -10e50, 10e10)
446- S2 = np .clip (S2 ,- 10e50 , 10e50 )
457+ S2 = np .clip (S2 ,- 10e30 , 10e30 )
447458
448- clipped_term2 = np .clip ((S2 * self .rng .uniform (0 ,1 )* (random_chicken_loc - self .M [particle ])),- 10e50 ,10e10 )
459+ clipped_term2 = np .clip ((S2 * self .rng .uniform (0 ,1 )* (random_chicken_loc - self .M [particle ])),- 10e30 ,10e10 )
449460term_2 = clipped_term2
450461
451462# new_loc = old_loc + term_1 + term_2
452- self .M [particle ]= self .M [particle ]+ term_1 + term_2
463+ self .M [particle ]= np . round ( self .M [particle ]+ term_1 + term_2 , self . number_decimals )
453464
454465
455466def move_chick (self ,particle ):
@@ -459,7 +470,7 @@ def move_chick(self, particle):
459470
460471mother_idx = int (self .chicken_info [particle ][2 ])# the the idx of the mother chicken
461472mother_loc = self .M [mother_idx ]
462- self .M [particle ]= self .M [particle ]+ self .rng .choice ([0 ,2 ])* (mother_loc - self .M [particle ])
473+ self .M [particle ]= np . round ( self .M [particle ]+ self .rng .choice ([0 ,2 ])* (mother_loc - self .M [particle ]), self . number_decimals )
463474
464475def reorganize_swarm (self ):
465476# rank the chickens' fitness vals and establish hierarchial order
@@ -559,12 +570,15 @@ def random_bound(self, particle):
559570# and may cause a buffer overflow with large exponents (a bug that was found experimentally)
560571update = self .check_bounds (particle )or not self .constr_func (self .M [particle ])
561572if update > 0 :
562- while (self .check_bounds (particle )> 0 )or (self .constr_func (self .M [particle ])== False ):
563- variation = self .ubound - self .lbound
564- self .M [particle ]= \
565- np .squeeze (self .rng .random ()*
566- np .multiply (np .ones ((1 ,np .shape (self .M )[1 ])),
567- variation )+ self .lbound )
573+ while (self .check_bounds (particle )> 0 )or (self .constr_func (self .M [particle ])== False ):
574+ variation = self .ubound - self .lbound
575+ self .M [particle ]= np .round (
576+ np .squeeze (
577+ self .rng .random ()*
578+ np .multiply (np .ones ((1 ,np .shape (self .M )[1 ])),variation )+
579+ self .lbound
580+ ),self .number_decimals )
581+
568582
569583def reflecting_bound (self ,particle ):
570584update = self .check_bounds (particle )