Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

3D array minimization (optimization)

Suppose I have the following 5x5x5 3D array, consisting of binary values:

space = [
[[0,1,0,0,1], [1,0,0,1,0], [0,1,0,1,1], [0,0,0,1,1], [0,1,1,0,1]],
[[1,1,1,0,1], [0,0,0,1,0], [0,0,1,1,1], [0,0,0,1,1], [0,1,0,0,0]],
[[0,1,0,1,0], [1,1,0,0,0], [1,0,0,1,0], [0,1,1,1,0], [0,1,1,1,1]],
[[0,1,0,1,0], [0,1,0,1,1], [1,1,0,1,0], [1,0,0,1,0], [0,0,0,0,0]],
[[1,0,0,1,1], [0,1,1,0,1], [0,1,0,1,1], [0,1,1,0,1], [1,0,1,0,0]],
]

and a function measure(space) which takes this 3D array as the input, and returns a real value. My goal is to find the best space configuration that returns the minimum measure() output.

How may I use scipy.optimize.minimize which takes a 1D-array as input (or any other function/library you might think is more appropriate for this problem) to solve this optimization problem?


EDIT: To clarify, the measure() function converts the 3D array into a CAD model (where 1: solid; 0: void), and passes the 3D geometry into an electromagnetic solver (antenna simulator) to get a result describing the "efficiency" of the antenna (sort of what the metric describes, except the lower the value is, the better the performance of the antenna).

like image 325
Coto TheArcher Avatar asked Aug 23 '26 12:08

Coto TheArcher


1 Answers

Don't use a 1d optimization function, there are at least three (surely more) approaches you can take:

  • Brute force, in your case that would be trying 2**125, which seems a bit too much.

  • Using MonteCarlo, i.e generating random solutions till finding the best, or at least one that is good enough

  • Using genetic algorithms, which will be probably the best you can get for this problem. You can use PyGAD for instance, and it won't take much time to get a good solution if not the best.

Here I put an example working where you only need to specify your fitness_function, in this case it will likely find the best solution.

import pygad
import numpy as np

space = [
[[0,1,0,0,1], [1,0,0,1,0], [0,1,0,1,1], [0,0,0,1,1], [0,1,1,0,1]],
[[1,1,1,0,1], [0,0,0,1,0], [0,0,1,1,1], [0,0,0,1,1], [0,1,0,0,0]],
[[0,1,0,1,0], [1,1,0,0,0], [1,0,0,1,0], [0,1,1,1,0], [0,1,1,1,1]],
[[0,1,0,1,0], [0,1,0,1,1], [1,1,0,1,0], [1,0,0,1,0], [0,0,0,0,0]],
[[1,0,0,1,1], [0,1,1,0,1], [0,1,0,1,1], [0,1,1,0,1], [1,0,1,0,0]],
]
space = np.array(space)

# I create a reference binary matrix to create a objective solution
i = np.identity(5)
ref = np.dstack([i]*5)

# flat your array to do it gen-like
space= space.flatten()
ref = ref.flatten()

def fitness_func(solution, solution_idx):
    # write here your fitness function, in my case i just compare how different two matrix are.
    fitness = np.sum(ref == solution)
    return fitness
    
fitness_function = fitness_func

num_generations = 400
num_parents_mating = 10

sol_per_pop = 14
num_genes = len(space)
init_range_low = 0
init_range_high = 1
gene_space=[0,1] # only binary solutions
parent_selection_type = "sss"
keep_parents = 8

crossover_type = "single_point" #"scattered" #
mutation_type = "random"
mutation_percent_genes = 1

ga_instance = pygad.GA(num_generations=num_generations,
                       num_parents_mating=num_parents_mating,
                       fitness_func=fitness_function,
                       sol_per_pop=sol_per_pop,
                       num_genes=num_genes,
                       init_range_low=init_range_low,
                       init_range_high=init_range_high,
                       gene_space=gene_space,
                       parent_selection_type=parent_selection_type,
                       keep_parents=keep_parents,
                       crossover_type=crossover_type,
                       mutation_type=mutation_type,
                       mutation_percent_genes=mutation_percent_genes)
                       
ga_instance.run()

solution, solution_fitness, solution_idx = ga_instance.best_solution()
print(f"Parameters of the best solution : {solution}")
print(f"Fitness value of the best solution = {solution_fitness}")

# reshape the solution 
solution = solution.reshape([5,5,5])
print(solution)

In general without knowing how "measure" the only approach that guarantee the best solution is brute force. If you know how "measure" looks like, using "maths" could be a fourth approach. But for most cases the genetic algorithm is a good enough solution for this optimization problem.

like image 157
Ziur Olpa Avatar answered Aug 25 '26 01:08

Ziur Olpa