import random import numpy as np import streamlit as st # Default parameter ranges default_param_ranges = { 'beef': (400, 600), 'cornflour': (80, 120), 'garlic': (2, 4), 'cilantro': (10, 20), 'salt': (0.5, 1.5), 'pepper': (0.25, 0.75), 'baking_powder': (0.25, 0.75) } # Explanations for each parameter param_explanations = { 'beef': 'Amount of beef in grams.', 'cornflour': 'Amount of cornflour in grams.', 'garlic': 'Number of garlic cloves.', 'cilantro': 'Amount of cilantro in grams.', 'salt': 'Amount of salt in teaspoons.', 'pepper': 'Amount of pepper in teaspoons.', 'baking_powder': 'Amount of baking powder in teaspoons.' } def generate_recipe(param_ranges): return {k: random.uniform(v[0], v[1]) for k, v in param_ranges.items()} def simulate_mixing(recipe, mix_factor): beef = recipe['beef'] cornflour = recipe['cornflour'] # Simulate mixing by dividing beef by cornflour and adding mix factor # If cornflour is 0, return 0 if cornflour == 0: return 0 mix_factor = (beef / cornflour) + mix_factor # If mix factor is greater than 1, return 1 if mix_factor > 1: return 1 return mix_factor def evaluate_squishiness(recipe): beef = recipe['beef'] cornflour = recipe['cornflour'] baking_powder = recipe['baking_powder'] squishiness = (beef / cornflour) + (baking_powder * 10) return squishiness def evaluate_meat_taste(recipe, taste_weights): beef = recipe['beef'] garlic = recipe['garlic'] cilantro = recipe['cilantro'] meat_taste = (beef * taste_weights['beef']) + (garlic * taste_weights['garlic']) + (cilantro * taste_weights['cilantro']) return meat_taste def evaluate_consistency(recipe, mix_factor): beef = recipe['beef'] cornflour = recipe['cornflour'] consistency = (beef / cornflour) * mix_factor return consistency def evaluate_fitness(recipe, weights, mix_factor, taste_weights): squishiness = evaluate_squishiness(recipe) meat_taste = evaluate_meat_taste(recipe, taste_weights) consistency = evaluate_consistency(recipe, mix_factor) fitness = (squishiness * weights['squishiness']) + (meat_taste * weights['meat_taste']) + (consistency * weights['consistency']) return fitness def genetic_algorithm(param_ranges, weights, pop_size=100, generations=50, mutation_rate=0.1, mix_factor=0.1, taste_weights=None): population = [generate_recipe(param_ranges) for _ in range(pop_size)] for gen in range(generations): fitness_scores = [evaluate_fitness(recipe, weights, mix_factor, taste_weights) for recipe in population] parents = select_parents(population, fitness_scores, pop_size // 2) offspring = [] for i in range(0, len(parents), 2): if i + 1 < len(parents): child = crossover(parents[i], parents[i + 1]) child = mutate(child, mutation_rate, param_ranges) offspring.append(child) population = parents + offspring best_recipe = select_parents(population, fitness_scores, 1)[0] return best_recipe def select_parents(population, fitness_scores, num_parents): parents_indices = np.argsort(fitness_scores)[-num_parents:] return [population[i] for i in parents_indices] def crossover(parent1, parent2): child = {} for param in parent1.keys(): child[param] = (parent1[param] + parent2[param]) / 2 return child def mutate(recipe, mutation_rate, param_ranges): for param in recipe.keys(): if random.random() < mutation_rate: recipe[param] += random.uniform(-0.1, 0.1) * recipe[param] recipe[param] = max(param_ranges[param][0], min(recipe[param], param_ranges[param][1])) return recipe # Streamlit app st.title('🧆 Meatball Recipe Simulator') # Custom parameter ranges st.sidebar.header('Custom Parameter Ranges') param_ranges = {} for param, (low, high) in default_param_ranges.items(): st.sidebar.markdown(f'**{param.capitalize()}**') st.sidebar.write(param_explanations[param]) low_val = st.sidebar.number_input(f'{param.capitalize()} Min', value=low, key=f'{param}_min') high_val = st.sidebar.number_input(f'{param.capitalize()} Max', value=high, key=f'{param}_max') param_ranges[param] = (low_val, high_val) # Weights for overall fitness st.sidebar.header('Weights for Fitness Evaluation') weights = { 'squishiness': st.sidebar.slider('Weight for Squishiness', 0.0, 1.0, 0.3), 'meat_taste': st.sidebar.slider('Weight for Meat Taste', 0.0, 1.0, 0.4), 'consistency': st.sidebar.slider('Weight for Consistency', 0.0, 1.0, 0.3) } # Weights for meat taste evaluation st.sidebar.header('Weights for Meat Taste Evaluation') taste_weights = { 'beef': st.sidebar.slider('Weight for Beef', 0.0, 1.0, 0.5), 'garlic': st.sidebar.slider('Weight for Garlic', 0.0, 1.0, 0.3), 'cilantro': st.sidebar.slider('Weight for Cilantro', 0.0, 1.0, 0.2) } # Other parameters pop_size = st.sidebar.slider('Population Size', 50, 200, 100) generations = st.sidebar.slider('Generations', 10, 100, 50) mutation_rate = st.sidebar.slider('Mutation Rate', 0.01, 0.2, 0.1) mix_factor = st.sidebar.slider('Mix Factor', 0.0, 1.0, 0.1) if st.button('Run Simulation'): with st.spinner('Running genetic algorithm...'): best_recipe = genetic_algorithm(param_ranges, weights, pop_size, generations, mutation_rate, mix_factor, taste_weights) st.subheader('Best Recipe') for ingredient, amount in best_recipe.items(): st.write(f'{ingredient.capitalize()}: {amount:.2f}') fitness = evaluate_fitness(best_recipe, weights, mix_factor, taste_weights) st.write(f'Fitness Score: {fitness:.2f}')