Spaces:
Sleeping
Sleeping
Commit
·
ff9b6c0
1
Parent(s):
1d3caec
Update src/backend/optimization_algo.py
Browse files- src/backend/optimization_algo.py +60 -46
src/backend/optimization_algo.py
CHANGED
@@ -5,7 +5,8 @@ import streamlit as st
|
|
5 |
# import all functions from src.backend.chatbot
|
6 |
from src.backend.chatbot import *
|
7 |
|
8 |
-
|
|
|
9 |
# Define the compatibility matrix
|
10 |
compatibility_matrix = st.session_state.full_mat
|
11 |
# Define the list of plants
|
@@ -16,31 +17,30 @@ def genetic_algorithm_plants():
|
|
16 |
num_plant_beds = st.session_state.n_plant_beds
|
17 |
# 1 <= min_species_per_bed <= max_species_per_bed <= len(user_plants)
|
18 |
min_species_per_bed = st.session_state.min_species
|
19 |
-
# max_species_per_bed >= floor(length(user_plants)-(min_species_per_bed*num_plant_beds-1) & max_species_per_bed <= len(user_plants)
|
20 |
max_species_per_bed = st.session_state.max_species
|
21 |
|
22 |
-
|
23 |
# Genetic Algorithm parameters
|
24 |
population_size = st.session_state.population_size
|
25 |
num_generations = st.session_state.num_generations
|
26 |
tournament_size = st.session_state.tournament_size
|
27 |
crossover_rate = st.session_state.crossover_rate
|
28 |
mutation_rate = st.session_state.mutation_rate
|
29 |
-
seed_population_rate = st.session_state.seed_population_rate
|
30 |
-
|
31 |
|
32 |
-
def generate_initial_population():
|
33 |
population = []
|
34 |
|
35 |
# Add seed groupings to the population, validated and replaced as necessary
|
36 |
-
num_seeds = int(
|
|
|
|
|
37 |
# we generate just one seed grouping for this beta language model suggestion feature
|
38 |
-
seed_grouping = get_language_model_suggestions()
|
39 |
if seed_grouping != "no response yet":
|
40 |
valid_seed_grouping = validate_and_replace(seed_grouping)
|
41 |
population.append(valid_seed_grouping)
|
42 |
|
43 |
-
|
44 |
# Fill the rest of the population with random groupings, also validated and replaced
|
45 |
while len(population) < population_size:
|
46 |
random_grouping = generate_random_grouping()
|
@@ -49,7 +49,6 @@ def genetic_algorithm_plants():
|
|
49 |
|
50 |
return population
|
51 |
|
52 |
-
|
53 |
def generate_random_grouping():
|
54 |
random.shuffle(user_plants)
|
55 |
remaining_plants = user_plants.copy()
|
@@ -67,7 +66,9 @@ def genetic_algorithm_plants():
|
|
67 |
num_species_in_bed = plants_per_bed
|
68 |
|
69 |
# Ensure the bed size is within the min and max constraints
|
70 |
-
num_species_in_bed = max(
|
|
|
|
|
71 |
|
72 |
bed = remaining_plants[:num_species_in_bed]
|
73 |
remaining_plants = remaining_plants[num_species_in_bed:]
|
@@ -75,8 +76,6 @@ def genetic_algorithm_plants():
|
|
75 |
|
76 |
return grouping
|
77 |
|
78 |
-
|
79 |
-
|
80 |
# Perform crossover between two parents, preserving at least one occurrence of each plant
|
81 |
def crossover(parent1, parent2):
|
82 |
if random.random() < crossover_rate:
|
@@ -88,17 +87,21 @@ def genetic_algorithm_plants():
|
|
88 |
for plant in user_plants:
|
89 |
if all(plant not in bed for bed in child1):
|
90 |
# Find a bed with fewer species and add the missing plant
|
91 |
-
min_bed_index = min(
|
|
|
|
|
92 |
child1[min_bed_index].append(plant)
|
93 |
if all(plant not in bed for bed in child2):
|
94 |
# Find a bed with fewer species and add the missing plant
|
95 |
-
min_bed_index = min(
|
|
|
|
|
96 |
child2[min_bed_index].append(plant)
|
97 |
|
98 |
return child1, child2
|
99 |
else:
|
100 |
return parent1, parent2
|
101 |
-
|
102 |
# Perform mutation on an individual, ensuring no bed exceeds the maximum species constraint
|
103 |
def mutate(individual):
|
104 |
if random.random() < mutation_rate:
|
@@ -110,8 +113,12 @@ def genetic_algorithm_plants():
|
|
110 |
species_in_bed = random.sample(species_in_bed, max_species_per_bed)
|
111 |
|
112 |
# Add missing plants by performing swaps between current species and missing plants
|
113 |
-
missing_plants = [
|
114 |
-
|
|
|
|
|
|
|
|
|
115 |
for _ in range(num_missing_plants):
|
116 |
swap_species = random.choice(missing_plants)
|
117 |
missing_plants.remove(swap_species)
|
@@ -124,8 +131,12 @@ def genetic_algorithm_plants():
|
|
124 |
|
125 |
# Calculate the fitness score of the grouping
|
126 |
def calculate_fitness(grouping):
|
127 |
-
positive_reward_factor =
|
128 |
-
|
|
|
|
|
|
|
|
|
129 |
|
130 |
# Define penalties for not meeting constraints
|
131 |
penalty_for_exceeding_max = 500 # Adjust as needed
|
@@ -144,15 +155,16 @@ def genetic_algorithm_plants():
|
|
144 |
species2_index = plant_list.index(species2_name)
|
145 |
|
146 |
# Compatibility score between two species in the same bed
|
147 |
-
compatibility_score = compatibility_matrix[species1_index][
|
148 |
-
|
|
|
|
|
149 |
if compatibility_score > 0:
|
150 |
# Positive reward for compatible species
|
151 |
-
score += compatibility_score*positive_reward_factor
|
152 |
elif compatibility_score < 0:
|
153 |
# Negative penalty for incompatible species
|
154 |
-
score += compatibility_score*negative_penalty_factor
|
155 |
-
|
156 |
|
157 |
# Apply penalties for not meeting constraints
|
158 |
if len(bed) > max_species_per_bed:
|
@@ -164,7 +176,6 @@ def genetic_algorithm_plants():
|
|
164 |
|
165 |
return score
|
166 |
|
167 |
-
|
168 |
# Perform tournament selection
|
169 |
def tournament_selection(population):
|
170 |
selected = []
|
@@ -189,11 +200,14 @@ def genetic_algorithm_plants():
|
|
189 |
individual[bed_idx] = species_in_bed
|
190 |
adjusted_offspring.append(individual)
|
191 |
|
192 |
-
return
|
|
|
|
|
|
|
193 |
|
194 |
# Genetic Algorithm main function
|
195 |
-
def genetic_algorithm():
|
196 |
-
population = generate_initial_population()
|
197 |
|
198 |
for generation in range(num_generations):
|
199 |
print(f"Generation {generation + 1}")
|
@@ -213,7 +227,6 @@ def genetic_algorithm_plants():
|
|
213 |
# Validate and replace any missing plants in the new population
|
214 |
population = [validate_and_replace(grouping) for grouping in population]
|
215 |
|
216 |
-
|
217 |
best_grouping = max(population, key=calculate_fitness)
|
218 |
best_grouping = validate_and_replace(best_grouping)
|
219 |
best_fitness = calculate_fitness(best_grouping)
|
@@ -224,7 +237,7 @@ def genetic_algorithm_plants():
|
|
224 |
# st.write(f"Best Grouping: {best_grouping}")
|
225 |
# st.write(f"Fitness Score: {best_fitness}")
|
226 |
return best_grouping
|
227 |
-
|
228 |
# def validate_and_replace(grouping):
|
229 |
# print("Grouping structure before validation:", grouping)
|
230 |
# all_plants = set(user_plants)
|
@@ -250,18 +263,20 @@ def genetic_algorithm_plants():
|
|
250 |
# random_bed[random.randint(0, len(random_bed) - 1)] = missing_plant
|
251 |
|
252 |
# return grouping
|
253 |
-
|
254 |
############
|
255 |
############ experimental
|
256 |
|
257 |
def adjust_grouping(grouping):
|
258 |
-
|
259 |
plants_in_grouping = set(plant for bed in grouping for plant in bed)
|
260 |
missing_plants = set(user_plants) - plants_in_grouping
|
261 |
|
262 |
for missing_plant in missing_plants:
|
263 |
# Find a bed that can accommodate the missing plant without exceeding max_species_per_bed
|
264 |
-
suitable_bed = next(
|
|
|
|
|
265 |
if suitable_bed is not None:
|
266 |
suitable_bed.append(missing_plant)
|
267 |
else:
|
@@ -272,16 +287,18 @@ def genetic_algorithm_plants():
|
|
272 |
# Ensure min_species_per_bed and max_species_per_bed constraints
|
273 |
for bed in grouping:
|
274 |
while len(bed) < min_species_per_bed:
|
275 |
-
additional_plant = random.choice(
|
|
|
|
|
276 |
bed.append(additional_plant)
|
277 |
while len(bed) > max_species_per_bed:
|
278 |
bed.remove(random.choice(bed))
|
279 |
|
280 |
return grouping
|
281 |
-
|
282 |
def validate_and_replace(grouping):
|
283 |
best_grouping = None
|
284 |
-
best_fitness = float(
|
285 |
|
286 |
for _ in range(5): # Generate 5 different configurations
|
287 |
temp_grouping = [bed.copy() for bed in grouping]
|
@@ -294,16 +311,13 @@ def genetic_algorithm_plants():
|
|
294 |
|
295 |
return best_grouping
|
296 |
|
297 |
-
|
298 |
-
|
299 |
############
|
300 |
-
def get_language_model_suggestions():
|
301 |
-
#
|
302 |
-
|
303 |
-
st.session_state.seed_groupings = get_seed_groupings_from_LLM()
|
304 |
return st.session_state.seed_groupings
|
305 |
|
|
|
306 |
|
307 |
-
|
308 |
-
best_grouping
|
309 |
-
return best_grouping
|
|
|
5 |
# import all functions from src.backend.chatbot
|
6 |
from src.backend.chatbot import *
|
7 |
|
8 |
+
|
9 |
+
def genetic_algorithm_plants(model, demo_lite):
|
10 |
# Define the compatibility matrix
|
11 |
compatibility_matrix = st.session_state.full_mat
|
12 |
# Define the list of plants
|
|
|
17 |
num_plant_beds = st.session_state.n_plant_beds
|
18 |
# 1 <= min_species_per_bed <= max_species_per_bed <= len(user_plants)
|
19 |
min_species_per_bed = st.session_state.min_species
|
20 |
+
# max_species_per_bed >= floor(length(user_plants)-(min_species_per_bed*num_plant_beds-1) & max_species_per_bed <= len(user_plants)
|
21 |
max_species_per_bed = st.session_state.max_species
|
22 |
|
|
|
23 |
# Genetic Algorithm parameters
|
24 |
population_size = st.session_state.population_size
|
25 |
num_generations = st.session_state.num_generations
|
26 |
tournament_size = st.session_state.tournament_size
|
27 |
crossover_rate = st.session_state.crossover_rate
|
28 |
mutation_rate = st.session_state.mutation_rate
|
29 |
+
seed_population_rate = st.session_state.seed_population_rate
|
|
|
30 |
|
31 |
+
def generate_initial_population(model, demo_lite):
|
32 |
population = []
|
33 |
|
34 |
# Add seed groupings to the population, validated and replaced as necessary
|
35 |
+
num_seeds = int(
|
36 |
+
population_size * st.session_state.seed_population_rate
|
37 |
+
) # 10% of the population as seeds
|
38 |
# we generate just one seed grouping for this beta language model suggestion feature
|
39 |
+
seed_grouping = get_language_model_suggestions(model, demo_lite)
|
40 |
if seed_grouping != "no response yet":
|
41 |
valid_seed_grouping = validate_and_replace(seed_grouping)
|
42 |
population.append(valid_seed_grouping)
|
43 |
|
|
|
44 |
# Fill the rest of the population with random groupings, also validated and replaced
|
45 |
while len(population) < population_size:
|
46 |
random_grouping = generate_random_grouping()
|
|
|
49 |
|
50 |
return population
|
51 |
|
|
|
52 |
def generate_random_grouping():
|
53 |
random.shuffle(user_plants)
|
54 |
remaining_plants = user_plants.copy()
|
|
|
66 |
num_species_in_bed = plants_per_bed
|
67 |
|
68 |
# Ensure the bed size is within the min and max constraints
|
69 |
+
num_species_in_bed = max(
|
70 |
+
min_species_per_bed, min(num_species_in_bed, max_species_per_bed)
|
71 |
+
)
|
72 |
|
73 |
bed = remaining_plants[:num_species_in_bed]
|
74 |
remaining_plants = remaining_plants[num_species_in_bed:]
|
|
|
76 |
|
77 |
return grouping
|
78 |
|
|
|
|
|
79 |
# Perform crossover between two parents, preserving at least one occurrence of each plant
|
80 |
def crossover(parent1, parent2):
|
81 |
if random.random() < crossover_rate:
|
|
|
87 |
for plant in user_plants:
|
88 |
if all(plant not in bed for bed in child1):
|
89 |
# Find a bed with fewer species and add the missing plant
|
90 |
+
min_bed_index = min(
|
91 |
+
range(len(child1)), key=lambda i: len(child1[i])
|
92 |
+
)
|
93 |
child1[min_bed_index].append(plant)
|
94 |
if all(plant not in bed for bed in child2):
|
95 |
# Find a bed with fewer species and add the missing plant
|
96 |
+
min_bed_index = min(
|
97 |
+
range(len(child2)), key=lambda i: len(child2[i])
|
98 |
+
)
|
99 |
child2[min_bed_index].append(plant)
|
100 |
|
101 |
return child1, child2
|
102 |
else:
|
103 |
return parent1, parent2
|
104 |
+
|
105 |
# Perform mutation on an individual, ensuring no bed exceeds the maximum species constraint
|
106 |
def mutate(individual):
|
107 |
if random.random() < mutation_rate:
|
|
|
113 |
species_in_bed = random.sample(species_in_bed, max_species_per_bed)
|
114 |
|
115 |
# Add missing plants by performing swaps between current species and missing plants
|
116 |
+
missing_plants = [
|
117 |
+
plant for plant in user_plants if plant not in species_in_bed
|
118 |
+
]
|
119 |
+
num_missing_plants = min(
|
120 |
+
len(missing_plants), max_species_per_bed - len(species_in_bed)
|
121 |
+
)
|
122 |
for _ in range(num_missing_plants):
|
123 |
swap_species = random.choice(missing_plants)
|
124 |
missing_plants.remove(swap_species)
|
|
|
131 |
|
132 |
# Calculate the fitness score of the grouping
|
133 |
def calculate_fitness(grouping):
|
134 |
+
positive_reward_factor = (
|
135 |
+
1000 # Adjust this to increase the reward for compatible species
|
136 |
+
)
|
137 |
+
negative_penalty_factor = (
|
138 |
+
2000 # Adjust this to increase the penalty for incompatible species
|
139 |
+
)
|
140 |
|
141 |
# Define penalties for not meeting constraints
|
142 |
penalty_for_exceeding_max = 500 # Adjust as needed
|
|
|
155 |
species2_index = plant_list.index(species2_name)
|
156 |
|
157 |
# Compatibility score between two species in the same bed
|
158 |
+
compatibility_score = compatibility_matrix[species1_index][
|
159 |
+
species2_index
|
160 |
+
]
|
161 |
+
|
162 |
if compatibility_score > 0:
|
163 |
# Positive reward for compatible species
|
164 |
+
score += compatibility_score * positive_reward_factor
|
165 |
elif compatibility_score < 0:
|
166 |
# Negative penalty for incompatible species
|
167 |
+
score += compatibility_score * negative_penalty_factor
|
|
|
168 |
|
169 |
# Apply penalties for not meeting constraints
|
170 |
if len(bed) > max_species_per_bed:
|
|
|
176 |
|
177 |
return score
|
178 |
|
|
|
179 |
# Perform tournament selection
|
180 |
def tournament_selection(population):
|
181 |
selected = []
|
|
|
200 |
individual[bed_idx] = species_in_bed
|
201 |
adjusted_offspring.append(individual)
|
202 |
|
203 |
+
return (
|
204 |
+
sorted_population[: population_size - len(adjusted_offspring)]
|
205 |
+
+ adjusted_offspring
|
206 |
+
)
|
207 |
|
208 |
# Genetic Algorithm main function
|
209 |
+
def genetic_algorithm(model, demo_lite):
|
210 |
+
population = generate_initial_population(model, demo_lite)
|
211 |
|
212 |
for generation in range(num_generations):
|
213 |
print(f"Generation {generation + 1}")
|
|
|
227 |
# Validate and replace any missing plants in the new population
|
228 |
population = [validate_and_replace(grouping) for grouping in population]
|
229 |
|
|
|
230 |
best_grouping = max(population, key=calculate_fitness)
|
231 |
best_grouping = validate_and_replace(best_grouping)
|
232 |
best_fitness = calculate_fitness(best_grouping)
|
|
|
237 |
# st.write(f"Best Grouping: {best_grouping}")
|
238 |
# st.write(f"Fitness Score: {best_fitness}")
|
239 |
return best_grouping
|
240 |
+
|
241 |
# def validate_and_replace(grouping):
|
242 |
# print("Grouping structure before validation:", grouping)
|
243 |
# all_plants = set(user_plants)
|
|
|
263 |
# random_bed[random.randint(0, len(random_bed) - 1)] = missing_plant
|
264 |
|
265 |
# return grouping
|
266 |
+
|
267 |
############
|
268 |
############ experimental
|
269 |
|
270 |
def adjust_grouping(grouping):
|
271 |
+
# Determine the plants that are missing in the grouping
|
272 |
plants_in_grouping = set(plant for bed in grouping for plant in bed)
|
273 |
missing_plants = set(user_plants) - plants_in_grouping
|
274 |
|
275 |
for missing_plant in missing_plants:
|
276 |
# Find a bed that can accommodate the missing plant without exceeding max_species_per_bed
|
277 |
+
suitable_bed = next(
|
278 |
+
(bed for bed in grouping if len(bed) < max_species_per_bed), None
|
279 |
+
)
|
280 |
if suitable_bed is not None:
|
281 |
suitable_bed.append(missing_plant)
|
282 |
else:
|
|
|
287 |
# Ensure min_species_per_bed and max_species_per_bed constraints
|
288 |
for bed in grouping:
|
289 |
while len(bed) < min_species_per_bed:
|
290 |
+
additional_plant = random.choice(
|
291 |
+
[plant for plant in user_plants if plant not in bed]
|
292 |
+
)
|
293 |
bed.append(additional_plant)
|
294 |
while len(bed) > max_species_per_bed:
|
295 |
bed.remove(random.choice(bed))
|
296 |
|
297 |
return grouping
|
298 |
+
|
299 |
def validate_and_replace(grouping):
|
300 |
best_grouping = None
|
301 |
+
best_fitness = float("-inf")
|
302 |
|
303 |
for _ in range(5): # Generate 5 different configurations
|
304 |
temp_grouping = [bed.copy() for bed in grouping]
|
|
|
311 |
|
312 |
return best_grouping
|
313 |
|
|
|
|
|
314 |
############
|
315 |
+
def get_language_model_suggestions(model, demo_lite):
|
316 |
+
# This returns a list of seed groupings based on the compatibility matrix
|
317 |
+
st.session_state.seed_groupings = get_seed_groupings_from_LLM(model, demo_lite)
|
|
|
318 |
return st.session_state.seed_groupings
|
319 |
|
320 |
+
# Run the genetic algorithm
|
321 |
|
322 |
+
best_grouping = genetic_algorithm(model, demo_lite)
|
323 |
+
return best_grouping
|
|