A first draft without images and examples for an introduction to GAs on my PhD Thesis.
GENERAL OVERVIEW
A genetic algorithm (GA) is (defined by Goldberg) a search algorithm that is inspired to the mechanics of natural evolution. (first part of Golberg definition) (an optimization technique, but Holland prefer to focus the attention on improvement)
In contrast with many others evolution-inspired algorithms that were studied starting from the 1950s for the optimization and machine learning, the theoretical and mathematical framework of Genetic Algorithms was developed by John Holland and his team in the 1960s, and finally formalized in 1975 (Holland), as the result of a more general study on the phenomenon of natural adaptation in order to simulate the biological evolution under computer systems. Originally defined by Holland as “genetic plans” they were soon renamed by his doctoral students, replacing the term “plan” with “algorithm”, in order to focus the attention on the role of computation. (introduction Holland)
Only in the 1980s genetic algorithms received an increasing recognition by scientists and studies ranging from biology, artificial intelligence, engineering and business to social sciences became to appear. (p.126 Goldberg) (see also Mitchell p.16)
WHY THEY ARE GOOD
When we deal with complex problems (nei quali è difficile trovare un metodo analitico di risoluzione), such as air-traffic programming, weather forecasts, share portfolios balance and electronic circuits design, at present genetic algorithms provide a tool to solve them and to find optimal or sub-optimal solutions.
Con riferimento al mondo delle costruzioni i problemi affrontati con queste tecniche riguardano l’ottimizzazione topologica di strutture di ponti e grandi luci in generale, la ricerca di forma di gusci e membrane, la risoluzione di problemi geometrici complessi come le strutture reciproche.
At present, genetic algorithms are used to deal with complex adaptive systems due to their robustness, efficiency and flexibility. (A good introduction to GAs is Golberg 1989, in which it is explained how they are robust and efficient.)
ELEMENTS OF A GENETIC ALGORITHM
Any clearly defined problem can be formulated in genetic terms (Koza, p.18) following the four major steps defined by Koza: (p.27)
- Determining the representation scheme
- Determining the fitness measure
- Determining the parameters and variables for controlling the algorithm
- Determining the way of designating the result and the criterion for terminating a run
TERMINOLOGY
The specific terminology for GAs derives from natural systems as well as from computer science technical vocabulary. For this reason, we find in technical literature the same thing with two different, but equivalent, terms. (p.21)
The followings are the main used terms:
- we can refer to specific genetic codes as “strings” basing on the computer science vocabulary. On the contrary as “chromosomes” with reference to the biological world;
- the general genetic constitution of an individual is called “structure” in computer science and “genotype” in biology;
- the actual observables characteristics of an individual are defined as “parameter set” or “solution alternative” or “point” in computer science. In natural systems we refer to them with the term “phenotype”;
- the basic unit of a genetic code is the “Gene” in natural systems and “(((X)))” in artificial;
- (((alleles (are the values) natural, features or detectors with different values)))
- the position of a gene in a genetic code is called “locus” in biology and “string position” in computer science.
- individual, population
It is important to avoid the mixed use of artificial and natural terminology at the same time. Indeed, in this thesis only the most common biological vocabulary will be used.
CLASSIFICATION (ref Tadei)
-evolutionary or not
- single solution multiple solution
- global search local search
After almost thirty year they have been described for the first time [Holland 1975], and after a large diffusion of theoretical studies and applications, the status of Genetic Algorithms (GA) in the scientific research still does not seem to be well defined. As many other mathematical models, starting from neural networks, going through fuzzy logic, simulated annealing techniques, and arriving to particle swarm optimization, ant colony optimization and the ’shuffled frog leaping’ algorithm, all these approaches to the solution of mathematical problems were born as a tentative to explain, or at least to imitate, some natural phenomena related to the world of life. The history of neural networks is quite interesting from this point of view: when they were described for the first time [Rumelhart and Mc Lelland, 1988] the contest was cognitive psychology and the interest to the parallel microstructure of thought was in opposition with a more synthetic “higher level” approach [Minsky, 1985]. Only a few years after the enthusiasm fade away, when scientists understood that having a local model of neuronal behavior is quite different from being able to simulate the parallel interactions between many billions of neurons.
In scientific literature we find papers on genetic algorithms under different branches: in Operation Research (OP) they are classified as a heuristic or semi-heuristic technique for combinatory optimization; in biology they are considered as a suitable model for living beings natural evolution. In architecture and structural engineering we are mainly concerned with their capability to explore a dominion of possible configurations looking for the best one, form a specific point of view defined in the so called fitness function.
DEVELOPMENT OF A GENETIC ALGORITHM
Il modo più semplice per comprendere il funzionamento di un algoritmo genetico è quello di provare a costruirne una semplice versione che risolva problemi molto semplici e facilmente definibili, come l’ordinamento di una serie di numeri. Il testo di riferimento consigliato per addentrarsi nell’argomento è sicuramente il “Manuale sulle reti neurali” di Floreano e Mattiussi, che spiega in maniera chiara appunto che cos’è un semplice algoritmo genetico. Un altro libro a vocazione didattica di facile lettura è “An introduction to Genetic Algorithms” di Mitchell. I libri ormai classici come Holland, Goldberg e Koza, presentano tutti un capitolo introduttivo che spiega, attraverso sempre la costruzione di un semplice algoritmo, la costruzione di un AG. La loro natura però più matematica e ingegneristica li porta inevitabilmente ad essere testi più difficili da approcciare, per lo meno all’inizio, provenendo da una formazione di architetto.
GA PROCEDURE
The pseudocode for a GA procedure is written in form of comments followed by the call of the relative function. In this way it is easier to modify each function on the basis of the specific problem leaving the same the general procedure.
* The generate geometry and performance functions depending on the basis of the problems. All the other operators need only a tuning phase in order to be tailored for the specific problem.
** Only if is used a binary coding. In architectural cases we use a real coding strategy because it is more efficient for the crossover operator (it does not cut coordinates).
Sub GA_Procedure() For each <iteration> of the GA procedure //Defined only with a specific reference problem Read input parameters (input geometry) Define the solutions domain // For each <individual> in the <population> //Generate the geometry in the graphic interface Call GenerateGeometry(<individual>)* //Calculate the <fitness> value of the individual Call Performance(<individual>)* Next //Sort the population individuals on the basis of their fitness Call SortPopulation() //Select individuals for reproduction Call RouletteWheelSelection() //Codify the design variables** Call DecimalToBinary** //Create a new population by means of a crossover reproduction Call GenerateNewPopulation() //Mutate randomly some genes of new individuals Call Mutations() //Decodify the design variables** Call BinaryToDecimal** Next End Sub
CODING STRATEGY
La scelta della strategia di codifica è molto importante perché da essa dipende in gran parte la corretta definizione del problema da risolvere e quindi l’efficienza generale dell’algoritmo.
La codifica binaria è sicuramente il modo più semplice per ottenere un codice genetico, ovvero un cromosoma, a partire da alcune variabili numeriche. Inoltre la sua somiglianza con il DNA del mondo naturale fa sì che sia il primo metodo di codifica introdotto nella letteratura di riferimento.
In architettura possiamo codificare distanze, coordinate e qualunque altra variabile numerica sotto forma binaria ma è da sottolineare quanto questo genere di codifica sia in realtà un passaggio superfluo e controproducente nella definizione di un problema in termini genetici. Infatti, la misura di una distanza oppure le coordinate di un punto nello spazio sono già di per sé delle codifiche di una più generale geometrica, non più in binario ma in reale.
Come vedremo parlando dell’operatore della riproduzione, il crossover, il sistema di codifica influisce sulle possibilità di ricombinazioni dei geni e la differenza tra la scelta di una codifica binaria o reale può cambiare di molto la situazione. Immaginando di dover quindi codificare le coordinate di una serie di punti di una geometria di riferimento, il loro valore reale si può considerare come minima entità inscindibile, modificabile e scambiabile, ma non divisibile.
Se infatti le prestazioni in termini ad esempio strutturali di una forma geometrica possono dipendere dalle coordinate di alcuni suoi punti che la definiscono, allora una codifica reale permette di ricombinare delle coordinate efficienti tra loro mentre al contrario una codifica binaria può frammentarle e crearne di nuove in modo assolutamente casuale. La codifica binaria di più coordinate consecutive infatti va a formare un codice genetico che può essere tagliato e ricombinato ad un livello inferiore del valore della coordinata, mentre una codifica reale no. (Da chiarire con figura).
Qui di seguito si riportano gli pseudocodici per costruire le funzioni di codifica e decodifica binaria. Per la codifica reale non è invece necessaria nessuna funzione ma basterà invece immagazzinare come variabile direttamente i valori numerici delle variabili.
Sub DecimalToBinary() //For one-dimensional chromosomes Make a <temp> temporary variable For each <individual> in the <population> For each <gene> of the <individual> Set <temp> equal to <gene> For each <bit> of the binary coding If <temp/2> is equal to the integer part of <temp/2> Then Set <temp> equal to <temp/2> Set <bit> equal to 0 Else Set <temp> equal to the integer part of <temp/2> Set <bit> equal to 1 End If Next Next Next End Sub BINARY DECODING SUBROUTINE Sub BinaryToDecimal() //For one-dimensional chromosomes For each <individual> in the <population> For each <gene> of the <individual> Set <gene> equal to 0 For each <bit> of the binary coding Set <gene> equal to <gene> + [2^(<bitLenght> - <bit>) * <bit>] Next Next Next End Sub
GA PARAMETERS
Ampiezza della popolazione
Numero di cicli iterativi
Percentuale dell’operatore di crossover
Percentuale di mutazioni
Numero di elementi di elite
Definizione del dominio e della sua ampiezza
Definizione di un restringimento del dominio sulla base della ricerca della soluzione
GA OPERATORS
I principali operatori di un algoritmo genetico sono tre: la selezione dei migliori individui, la riproduzione degli individui selezionati, l’applicazione di mutazioni genetiche casuali sulla nuova popolazione. Oltre a questi operatori classici si possono aggiungere dei criteri secondari di generale miglioramento dell’efficienza dell’algoritmo. Ne verrà utilizzato soltanto uno, l’elitismo, che consiste nella conservazione da una generazione all’altra di una serie di migliori individui, senza che questi vengano intaccati dalle operazioni degli operatori. In questo modo si evita che il processo evolutivo possa prendere strade involutive o derivare. (Da ampliare)
SELECTION
Il più tradizionale criterio di selezione degli individui di una popolazione è la ruota della fortuna truccata [Rif. Floreano, Goldberg, Koza]. E’ una tecnica che corrisponde figurativamente ad effettuare dei lanci di una roulette che riporta indicati tutti gli individui della popolazione, separati tra loro però non da intervalli regolari come accade nei classici tavoli da casinò ma piuttosto da spazi tanto ampi quanta è la loro probabilità di essere selezionati, calcolata in proporzione alla loro performance. In questo modo, seppur ogni lancio della ruota sia casuale, la probabilità di uscita dei migliori individui è maggiore rispetto ai peggiori, e quindi essi saranno più probabilmente genitori di nuovi individui, tendenzialmente ancora migliori.
Di seguito si riporta lo pseudocodice per costruirsi una ruota della fortuna truccata, che assegni la probabilità di selezione di un individuo sulla base della sua performance, in termini genetici sempre definita come fitness, e per questo calcolo utilizzata nella sua versione normalizzata. (Descrivere meglio con aiuto di immagini)
Sub RouletteWheelSelection() Make <rand> as a random variable Make <sumExpValue> as the sum of expected values Set <sumExpValue> equal to 0 For each <individual> in the <population> Set the <individual expValue> equal to the <individual normalized fitness> Sum the <individual expValue> to <sumExpValue> Next For each <individual> in the <population> excluding <numElite> best individuals Set <rand> equal to a random value chosen from 0 to <sumExpValue> Make a <counter> variable equal to 0 Make a <chosenExpValue> variable equal to 0 While <chosenExpValue> is smaller than <rand> Set <counter> equal to <counter> + 1 Sum <chosenExpValue> with the <individual expValue> Loop Copy the chosen individual <counter> in the current position of the population Next End Sub
REPRODUCTION
SINGLE POINT OR TWO POINTS CROSSOVER
Sub GenerateNewPopulation() //It is possible to generate more than one cutting point Make a <ptCross1> variable that define a first cutting point of the <individual chromosome> For each <reproduction operation> in the <population> Select randomly a first parent <parent1> in the <population> Select randomly a second parent <parent2> in the <population> If <parent2> is equal to <parent1> Then Select randomly another <parent2> End If //Combine parents genes to create a new <individual> //This Function can be called more than one time Call Crossover(<parent1>, <parent2>, <position of child1> in the <population>, <position of child2> in the <population>, <ptcross1>) Next End Sub Sub Crossover(<parentA>, <parentB>, <childA>, <childB>, <ptCrossA>) For each <gene> in the <chromosome> of the <parentA> If <gene position> is smaller than <ptCrossA> Then Copy <parentA gene> in the same position into <childA gene> Copy <parentB gene> in the same position into <childB gene> Else Copy <parentA gene> in the same position into <childB gene> Copy <parentB gene> in the same position into <childA gene> End If Next End Sub TWO-DIMENSIONAL CROSSOVER OPERATOR, REAL CODING Sub GenerateNewPopulation() //Cutting points for a two-dimensional chromosome Make a <ptCross1> variable that define a first cutting point of the <individual chromosome> Make a <ptCross2> variable that define a second cutting point of the <individual chromosome> For each <reproduction operation> in the <population> Select randomly a first parent <parent1> in the <population> Select randomly a second parent <parent2> in the <population> If <parent2> is equal to <parent1> Then Select randomly another <parent2> End If //Combine parents genes to create a new <individual> //This Function can be called more than one time Call Crossover(<parent1>, <parent2>, <position of child1> in the <population>, <position of child2> in the <population>, <ptcross1>, <ptcross2>) Next End Sub Sub Crossover(<parentA>, <parentB>, <childA>, <childB>, <ptCrossA>, <ptCrossB>) //Two-dimensional crossover operator For each <gene> in the matrix rows of the <parentA> For each <gene> in the matrix columns of the <parentA> If <gene row position> is smaller than <ptCrossA> Then If <gene column position> is smaller than <ptCrossB> Then Copy <parentA gene> in the same position into <childA gene> Copy <parentB gene> in the same position into <childB gene> Else Copy <parentA gene> in the same position into <childB gene> Copy <parentB gene> in the same position into <childA gene> End If Else If <gene column position> is smaller than <ptCrossB> Then Copy <parentA gene> in the same position into <childB gene> Copy <parentB gene> in the same position into <childA gene> Else Copy <parentA gene> in the same position into <childA gene> Copy <parentB gene> in the same position into <childB gene> End If End If Next Next End Sub
MUTATION
L’operatore delle mutazioni genetiche casuali è molto importante in quanto è la seconda causa di miglioramento delle performance di una popolazione di individui dopo la ricombinazione genetica realizzata attraverso il crossover. In certi casi, una piccola mutazione relativa ad un singolo gene può essere più efficace rispetto alla ricombinazione dei geni di due buoni genitori e per questo è un operatore che va inserito all’interno della procedura in bassa percentuale in modo da non essere controproducente.
Nei capitoli dedicati ai casi studio, vedremo come implementare questo operatore in modo più efficiente in modo da effettuare contemporaneamente una ricerca della soluzione migliore sia nell’intero dominio delle soluzioni che nel più circoscritto insieme delle soluzioni migliori.
Sub Mutations() For each <mutation> in the <individuals> of the <population> Select randomly an <individual> in the <population> excluding <numElite> best individuals Select randomly a <gene> in the <individual chromosome> //It depends on the coding strategy Generate randomly a <new gene> value in the solutions domain Substitute the <new gene> value with the old <gene> value Next End Sub
TUNING PROBLEMS
Ogni singolo problema approcciato con l’ausilio di un algoritmo genetico richiede una specifica fase di messa a punto della procedura di ottimizzazione. Non è possibile definire delle linee guida generali per migliorare l’efficienza dell’algoritmo ma volta per volta è necessario affinare da sé la definizione del problema da risolvere, scegliere accuratamente la strategia di codifica, il dominio delle soluzioni e l’utilizzo bilanciato degli operatori principali. Per chiarire meglio l’importanza di questa fase, nei successivi capitoli dedicati alla sperimentazione attraverso dei casi studio verrà dedicato ampio spazio alla descrizione di come si è svolta questa delicata fase.
OTHER FUNCTIONS
Un algoritmo genetico richiede generalmente la costruzione di una serie di funzioni accessorie, come ad esempio le funzioni di stampa, indispensabili per leggere e analizzare i risultati della procedura. Però tra queste si intende riportare una funzione necessaria, ovvero l’ordinamento degli individui di una popolazione sulla base delle prestazioni valutate.
Il metodo più semplice per ordinare gli individui consiste nel prendere a riferimento il vettore delle fitness e, partendo dal primo valore, confrontarlo con il suo immediato vicino. Se maggiore di esso li si scambia di posizione nel vettore altrimenti si procede al prossimo valore. Si procede al contrario invece qualora si preferisca ordinare in ordine decrescente i valori, come tra l’altro è consuetudine fare in questo tipo di algoritmi (l’individuo migliore ha sempre fitness più bassa). Di seguito si riporta lo pseudocodice.
Sub SortPopulation() //Numerical sorting in descending order For each <individual> in the <population> If the <fitness of individual> is smaller than the <fitness of next individual> Then Switch the <position of evaluated individuals> in the <population> Else Nothing End If Next End Sub












