Skip to content

Commit

Permalink
paper;figure
Browse files Browse the repository at this point in the history
  • Loading branch information
Freakwill committed May 21, 2024
1 parent 8340ff7 commit 8429da5
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 20 deletions.
33 changes: 13 additions & 20 deletions paper/paper.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ output:

Leveraging the principles of object-oriented programming (OOP) and the meta-programming, we introduce a distinctive design paradigm is coined as "algebra-inspired Programming" signifying the fusion of algebraic methodologies with the software architecture.


# Statement of need

As one of the earliest developed optimization algorithms [@holland; @katoch], the genetic algorithm (GA) has found extensive application across various domains and has undergone modifications and integrations with new algorithms [@alam; @cheng; @katoch]. The principles of GA will not be reviewed in this article. For a detailed understanding, please refer to references [@holland; @simon] and the associated literatures.
Expand All @@ -45,7 +44,6 @@ In a typical Python implementation, populations are initially defined as lists o

A concise comparison between `pyrimidine` and several popular frameworks provided in \autoref{frameworks}, such as [`DEAP`](https://deap.readthedocs.io/) [@fortin] and [`gaft`](https://github.com/PytLab/gaft), which have significantly influenced the design of `pyrimidine`.


: Comparison of the popular genetic algorithm frameworks. []{label="frameworks"}

<!-- +-------------------+------------+----------+----------+----------+ -->
Expand All @@ -67,7 +65,9 @@ A concise comparison between `pyrimidine` and several popular frameworks provide

`Pyrimidine` fully utilizes the OOP and meta-programming capabilities of Python, making the design of the APIs and the extension of the program more natural. So far, We have implemented a variety of intelligent algorithms by `pyrimidine`, including adaptive GA [@hinterding], quantum GA [@supasil], differential evolution [@radtke], evolutionary programming, particle swarm optimization [@wang], as well as some local search algorithms, such as simulated annealing.

This library provides a wide range of chromosome classes to use, including Boolean, integer, and real number types, and that can even represent probability distributions and node permutations in graph and their mixed forms. Most of them are subclasses of `numpy.ndarray`, the array class of `numpy`, but custom definitions are also allowed. Each class implements corresponding genetic operations such as crossover and others.
This library provides a wide range of chromosome classes in the `chromosome` module, including Boolean, integer, and real number types, and that can even represent probability distributions and node permutations in graph and their mixed forms. Most of them are subclasses of `numpy.ndarray`, the array class of `numpy`, but custom definitions are also allowed. Each class implements corresponding genetic operations such as crossover and others.

In the `benchmarks` module, we offer a comprehensive array of problems to evaluate various algorithms, encompassing both traditional optimization models and cutting-edge machine learning models.

# Algebra-inspired programming

Expand Down Expand Up @@ -158,7 +158,8 @@ class UserChromosome(BaseChromosome):
def _fitness(self):
# Compute the fitness

# population as a container of chromosomes, instead of individuals
# population as a container of chromosomes,
# instead of individuals
UserPopulation = StandardPopulation[UserChromosome] // 10
```

Expand Down Expand Up @@ -188,7 +189,8 @@ class UserIndividual(MonoIndividual):

"""
equivalent to:
UserIndividual = MonoIndividual[BinaryChromosome // n].set_fitness(lambda o: _evaluate(o[0]))
UserIndividual = MonoIndividual[BinaryChromosome // n]
.set_fitness(lambda o: _evaluate(o[0]))
"""

UserPopulation = StandardPopulation[UserIndividual] // 20
Expand All @@ -209,35 +211,26 @@ Finally, the optimal individual can be obtained with `pop.best_individual`, or `

# Visualization

Instead of implementing visualization methods, `pyrimidine` yields a `pandas.DataFrame` object that encapsulates statistical results for each generation by setting `history=True` in `evolve` method. Users can harness this object to plot the performance curves. Generally, users are required to furnish a "statistic dictionary" whose keys are the names of the statistics, and values are functions mapping the population to numerical values, or strings presenting pre-defined methods or attributes of the population.
Instead of implementing visualization methods, `pyrimidine` yields a `pandas.DataFrame` object that encapsulates statistical results for each generation by setting `history=True` in the `evolve` method. Users can harness this object to plot the performance curves. Generally, users are required to furnish a "statistic dictionary" whose keys are the names of the statistics, and values are functions mapping the population to numerical values, or strings presenting pre-defined methods or attributes of the population.

```python
# statistic dictionary, computing the mean, the maximum and the standard deviation of the fitnesses for each generation
# statistic dictionary, computing the mean, the maximum and
# the standard deviation of the fitnesses for each generation
stat = {'Mean Fitness': 'mean_fitness',
'Best Fitness': 'max_fitness',
'Standard Deviation of Fitnesses': lambda pop: np.std(pop.get_all_fitness())
}

# obtain the statistical results through the evolution.
data = pop.evolve(stat=stat, n_iter=100, history=True)

# Utilize the `plot` method of the `pandas.DataFrame` object to draw separate plots for the fitness values and the standard deviations of the population.
```

<!-- import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax2 = ax.twinx()
data[['Mean Fitness', 'Best Fitness']].plot(ax=ax)
ax.legend(loc='upper left')
data['Standard Deviation of Fitnesses'].plot(ax=ax2, style='y-.')
ax2.legend(loc='lower right')
ax.set_xlabel('Generations')
ax.set_ylabel('Fitness')
plt.show() -->
`data` is an `pandas.DataFrame` object, with the columns "Mean Fitness", "Best Fitness" and "Standard Deviation of Fitnesses". Now utilize the `plot` method of the object (or the Python library `matplotlib`) to show the iteration history.

![The fitness evolution curve of the population.](plot-history.png)

You can also set `verbose=True` in the `evolve` method to see each step of the iteration. If you do not want to set anything, then it is recommended to use the `ezolve` method, such as `pop.ezolve()`.

<!--
# Create your own classes and algorithms
Expand Down
5 changes: 5 additions & 0 deletions pyrimidine/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ def equal_to(self, other):
@classmethod
def random(cls, *args, **kwargs):
raise NotImplementedError

def op(self, s):
def _f(*args, **kwargs):
return getattr(self._population, s)(self, *args, **kwargs)
return _f


class BaseIndividual(FitnessMixin, metaclass=MetaContainer):
Expand Down

0 comments on commit 8429da5

Please sign in to comment.