This algorithm manipulates cuts by means of its associated binary
sequences defined as follows. Each cut K is defined by its
associated vertex subset S of V(G): K contains all edges
joining vertices inside S with vertices outside S. If
|V(G)|=n, we can construct a n-bit binary sequence b =
(b1,b2,...,bn) with bi = 1 if vertex vi belongs to S, and 0
otherwise.
The genetic algorithm consists of starting with a cut population,
where each cut is represented by its corresponding binary
sequence defined above, and thus the population is simply a
binary matrix. This initial cut population can be provided by
the user or can be random. The initial population can be the
output of a previous run of the genetic algorithm, thus
allowing a chained execution. Then the routine sequentially
perform over the cuts of the population the
crossover, mutation, local search
and selection operations.
The crossover operation takes two cuts as "parents" and
forms two "offsprings" by cutting and interchanging the binary
sequences of the parents; see crossover_sequences for more
information.
The mutation operation performs a "small" perturbation of
each cut trying to escape from local optima. It uses a random
flip on each bit of the binary sequence associated with the
cut, see mutate_binary_sequence for more information.
The local search operation takes the cuts found by the
crossover and mutation operations and improves them using some
local search heuristic, in this case improve_cut_flip. This
allows this algorithm to approach local maxima faster.
The selection operation is used when selecting pairs of
parents for crossover and when selecting individuals to form
the population for the next generation. In both cases, it
uses a probability exponential in the weight with rate
parameter "beta", favouring the better fitted to be selected.
Lower values of beta favours the inclusion of cuts with worse
fitting function values. When selecting the next population,
the selection uses elitism, which is to save the best
fitted individuals to the next generation; this is controlled
with parameter "elite".
The usefulness of the crossover and mutation operations stems from
its ability to escape from the local maxima. Of course, more
iterations (Ngen) and larger populations (Npop) might improve
the result, but recall that no random algorithm can guarantee
to find the optimum of a given Max-Cut instance.
This algorithm calls many times the routines compute_cut_weight,
crossover_sequences, mutate_binary_sequence and
improve_cut_flip; therefore, it is not especially efficient
when called on large problems or with high populations or many
generations. Please consider chaining the algorithm: perform
short runs, using the output of a run as the input of the
next.