# Updated Implementation includes RandomSearch, Best Individual .npy save import numpy as np import tensorflow as tf import tensorflow_probability as tfp import plotly.graph_objs as go from keras_tuner import HyperModel, RandomSearch from tqdm import tqdm # Define the target X-shape pattern target_pattern = np.array([ [25.76815, -80.1868], [25.7743, -80.1937], [25.762, -80.18], [25.76815, -80.1868], [25.7743, -80.18], [25.762, -80.1937] ]) # Convert target pattern to TensorFlow tensor target_pattern_tf = tf.constant(target_pattern, dtype=tf.float32) # Define the GA parameters ranges population_sizes = [30, 50, 70] num_generations = 100000 mutation_rates = [0.05, 0.1, 0.15] crossover_rates = [0.7, 0.8, 0.9] elitism_counts = [3, 5, 7] # Define the fitness function using TensorFlow def fitness_function(positions): return tf.reduce_sum((positions - target_pattern_tf) ** 2, axis=[1, 2]) # Selection function (tournament selection) using TensorFlow def selection(population, fitness_values): selected = [] for _ in range(len(population)): idx1, idx2 = tf.random.uniform(shape=(2,), minval=0, maxval=len(population), dtype=tf.int32) if fitness_values[idx1] < fitness_values[idx2]: selected.append(population[idx1]) else: selected.append(population[idx2]) return tf.stack(selected) # Crossover function (single-point crossover) using TensorFlow def crossover(parent1, parent2, crossover_rate): if tf.random.uniform(()) < crossover_rate: crossover_point = tf.random.uniform((), minval=1, maxval=len(parent1), dtype=tf.int32) child1 = tf.concat([parent1[:crossover_point], parent2[crossover_point:]], axis=0) child2 = tf.concat([parent2[:crossover_point], parent1[crossover_point:]], axis=0) return child1, child2 else: return parent1, parent2 # Mutation function using TensorFlow Probability def mutate(individual, mutation_rate): mutation_mask = tfp.distributions.Bernoulli(probs=mutation_rate).sample(sample_shape=tf.shape(individual)) mutation_noise = tfp.distributions.Normal(loc=0.0, scale=0.1).sample(sample_shape=tf.shape(individual)) return individual + tf.cast(mutation_mask, tf.float32) * mutation_noise # GA algorithm using TensorFlow def genetic_algorithm(population_size, mutation_rate, crossover_rate, elitism_count, num_generations): population = tf.random.uniform((population_size, len(target_pattern), 2), dtype=tf.float32) best_fitness_overall = float('inf') best_individual_overall = None for generation in tqdm(range(num_generations), desc="Generations"): fitness_values = fitness_function(population) # Track the best fitness and individual overall best_fitness_current_gen = tf.reduce_min(fitness_values).numpy() best_individual_current_gen = population[tf.argmin(fitness_values)].numpy() if best_fitness_current_gen < best_fitness_overall: best_fitness_overall = best_fitness_current_gen best_individual_overall = best_individual_current_gen # Elitism: Preserve the best individuals elite_indices = tf.argsort(fitness_values)[:elitism_count] elites = tf.gather(population, elite_indices) # Select parents parents = selection(population, fitness_values) # Create offspring through crossover and mutation offspring = [] for i in range(0, len(parents) - 1, 2): child1, child2 = crossover(parents[i], parents[i + 1], crossover_rate) offspring.append(mutate(child1, mutation_rate)) offspring.append(mutate(child2, mutation_rate)) # Ensure offspring size matches population size - elitism count num_offspring_needed = population_size - elitism_count offspring = offspring[:num_offspring_needed] # Replace the population with the offspring and elites population = tf.concat([tf.stack(offspring), elites], axis=0) return best_fitness_overall, best_individual_overall # HyperModel for Keras Tuner class GAHyperModel(HyperModel): def __init__(self, num_generations): self.num_generations = num_generations def build(self, hp): population_size = hp.Choice('population_size', values=population_sizes) mutation_rate = hp.Choice('mutation_rate', values=mutation_rates) crossover_rate = hp.Choice('crossover_rate', values=crossover_rates) elitism_count = hp.Choice('elitism_count', values=elitism_counts) final_fitness, _ = genetic_algorithm( population_size, mutation_rate, crossover_rate, elitism_count, self.num_generations ) return final_fitness # Hyperparameter tuning using Keras Tuner tuner = RandomSearch( GAHyperModel(num_generations), objective='val_loss', max_trials=50, executions_per_trial=1, directory='ga_tuning', project_name='genetic_algorithm' ) tuner.search(x=None, y=None, epochs=1, verbose=1) # Get the best hyperparameters best_hyperparams = tuner.get_best_hyperparameters(num_trials=1)[0] best_population_size = best_hyperparams.get('population_size') best_mutation_rate = best_hyperparams.get('mutation_rate') best_crossover_rate = best_hyperparams.get('crossover_rate') best_elitism_count = best_hyperparams.get('elitism_count') # Run the GA with the best hyperparameters best_fitness, best_individual = genetic_algorithm( best_population_size, best_mutation_rate, best_crossover_rate, best_elitism_count, num_generations ) # Output the best hyperparameters and the corresponding best individual print(f"Best hyperparameters: population_size={best_population_size}, mutation_rate={best_mutation_rate}, crossover_rate={best_crossover_rate}, elitism_count={best_elitism_count}") print(f"Best fitness: {best_fitness}") # Save the best individual np.save('best_individual.npy', best_individual) # Validate the flight path trajectory def plot_trajectory(positions): fig = go.Figure() # Plot target pattern fig.add_trace(go.Scatter( x=target_pattern[:, 0], y=target_pattern[:, 1], mode='markers+lines', name='Target Pattern', marker=dict(size=10, color='red') )) # Plot optimized pattern fig.add_trace(go.Scatter( x=positions[:, 0], y=positions[:, 1], mode='markers+lines', name='Optimized Pattern', marker=dict(size=10, color='blue') )) fig.update_layout( title='UAV Swarm Intelligence: X-Shape Pattern', xaxis_title='X', yaxis_title='Y' ) fig.show() # Plot the final optimized pattern plot_trajectory(best_individual)