diff --git a/docs/build/.doctrees/environment.pickle b/docs/build/.doctrees/environment.pickle index 24b51fb..83d0019 100644 Binary files a/docs/build/.doctrees/environment.pickle and b/docs/build/.doctrees/environment.pickle differ diff --git a/docs/build/.doctrees/source/Customization.doctree b/docs/build/.doctrees/source/Customization.doctree index b149a7f..167bd7b 100644 Binary files a/docs/build/.doctrees/source/Customization.doctree and b/docs/build/.doctrees/source/Customization.doctree differ diff --git a/docs/build/.doctrees/source/Examples.doctree b/docs/build/.doctrees/source/Examples.doctree index eba190e..01b0be1 100644 Binary files a/docs/build/.doctrees/source/Examples.doctree and b/docs/build/.doctrees/source/Examples.doctree differ diff --git a/docs/build/.doctrees/source/Helpers.doctree b/docs/build/.doctrees/source/Helpers.doctree index 54004a8..be62900 100644 Binary files a/docs/build/.doctrees/source/Helpers.doctree and b/docs/build/.doctrees/source/Helpers.doctree differ diff --git a/docs/build/.doctrees/source/Install.doctree b/docs/build/.doctrees/source/Install.doctree index 2ce56a2..d44b22a 100644 Binary files a/docs/build/.doctrees/source/Install.doctree and b/docs/build/.doctrees/source/Install.doctree differ diff --git a/docs/build/.doctrees/source/Misc.doctree b/docs/build/.doctrees/source/Misc.doctree index 945eb58..17de073 100644 Binary files a/docs/build/.doctrees/source/Misc.doctree and b/docs/build/.doctrees/source/Misc.doctree differ diff --git a/docs/build/.doctrees/source/pyrimidine.benchmarks.doctree b/docs/build/.doctrees/source/pyrimidine.benchmarks.doctree index ddd3b3f..934d272 100644 Binary files a/docs/build/.doctrees/source/pyrimidine.benchmarks.doctree and b/docs/build/.doctrees/source/pyrimidine.benchmarks.doctree differ diff --git a/docs/build/.doctrees/source/pyrimidine.doctree b/docs/build/.doctrees/source/pyrimidine.doctree index e64ec14..c65cbb5 100644 Binary files a/docs/build/.doctrees/source/pyrimidine.doctree and b/docs/build/.doctrees/source/pyrimidine.doctree differ diff --git a/docs/build/.doctrees/source/pyrimidine.local_search.doctree b/docs/build/.doctrees/source/pyrimidine.local_search.doctree index 2cf80ee..9de8d0a 100644 Binary files a/docs/build/.doctrees/source/pyrimidine.local_search.doctree and b/docs/build/.doctrees/source/pyrimidine.local_search.doctree differ diff --git a/docs/build/_sources/source/Customization.md.txt b/docs/build/_sources/source/Customization.md.txt index 5538831..2f345d0 100644 --- a/docs/build/_sources/source/Customization.md.txt +++ b/docs/build/_sources/source/Customization.md.txt @@ -13,12 +13,13 @@ class _Particle(BaseParticle): ... class MyParticleSwarm(ParticleSwarm, metaclass=MetaContainer): + element_class = _Particle default_size = 20 ... ``` -In the standard definition, as an individual, a particle has two "chromosomes", one represents the current position, the other represents the current velocity. While, you can define three or more chromosomes, to include the acceleration. It also has an important attribute, `memory` as its clone, but stores the best position that the particle passed-by. +In the standard definition, as an individual, a particle has two "chromosomes", one represents the current position, the other represents the current velocity. While, you can define three or more chromosomes, to include the acceleration. It also has an important attribute, `memory` storing the best position that the particle passed-by. ## Simulated Annealing Algorithm @@ -45,7 +46,7 @@ class SimulatedAnnealing(FitnessModel): } def init(self): - self.phantom = self.clone(fitness=None) + self.phantom = self.copy(fitness=None) def transition(self, *args, **kwargs): T = self.initT @@ -145,7 +146,7 @@ class SimulatedAnnealing(PhantomIndividual): def init(self): # initialize phantom solution - self.phantom = self.clone(fitness=None) + self.phantom = self.copy(fitness=None) def transit(self, *args, **kwargs): diff --git a/docs/build/_sources/source/Examples.md.txt b/docs/build/_sources/source/Examples.md.txt index 190e85e..4b9aba0 100644 --- a/docs/build/_sources/source/Examples.md.txt +++ b/docs/build/_sources/source/Examples.md.txt @@ -1,11 +1,11 @@ -# Examples and Comparison of Algorithm +# Examples and Comparison of Algorithms [TOC] -## Examples +## Example 1 ### A simple example --- Knapsack problem -One of the famous problem is the knapsack problem. It is a good example for GA. +One of the well-known problem is the knapsack problem. It is a good example for GA. #### Codes @@ -24,13 +24,13 @@ _evaluate = Knapsack.random(n_bags) # : 0-1 array -> float # Define the individual class class MyIndividual(MonoIndividual): - element_class = BinaryChromosome.set(default_size=n_bags) + element_class = BinaryChromosome // n_bags def _fitness(self) -> float: # To evaluate an individual! return _evaluate(self.chromosome) """ Equiv. to - MyIndividual = MonoIndividual[BinaryChromosome.set(default_size=n_bags)].set_fitness(_evaluate) + MyIndividual = MonoIndividual[BinaryChromosome//n_bags].set_fitness(_evaluate) """ # Define the population class @@ -39,9 +39,9 @@ class MyPopulation(HOFPopulation): default_size = 10 """ Equiv. to - MyPopulation = HOFPopulation[MyIndividual].set(default_size=10) + MyPopulation = HOFPopulation[MyIndividual] //10 or, as a population of chromosomes - MyPopulation = HOFPopulation[BinaryChromosome.set(default_size=n_bags).set_fitness(_evaluate)].set(default_size=10) + MyPopulation = HOFPopulation[(BinaryChromosome//n_bags).set_fitness(_evaluate)] //10 """ pop = MyPopulation.random() @@ -153,9 +153,9 @@ iteration & solution & Mean Fitness & Best Fitness & Standard Deviation of Fitne ``` -## Create new algo. +## Example 2 -In the following example, the binary chromosomes should be decoded to floats. We recommend `digit_converter`, created by the author for such purpose, to handle with it. +In the following example, the binary chromosomes should be decoded to floats. We recommend `digit_converter` to handle with it, created by the author for such purpose. ```python #!/usr/bin/env python3 @@ -168,7 +168,7 @@ from digit_converter import * ndim = 10 def evaluate(x): - return -rosenbrock(ndim)(x) + return -rosenbrock(x) class _Chromosome(BinaryChromosome): @@ -184,12 +184,12 @@ class uChromosome(BinaryChromosome): def _fitness(i): return evaluate(i.decode()) -ExampleIndividual = MultiIndividual[_Chromosome].set_fitness(_fitness) +ExampleIndividual = MultiIndividual[_Chromosome].set_fitness(_fitness) // ndim -class MyIndividual(MixIndividual[(_Chromosome,)*ndim + (uChromosome,)].set_fitness(_fitness)): - """my own individual class +class MyIndividual(MixedIndividual[(_Chromosome,)*ndim + (uChromosome,)].set_fitness(_fitness)): + """My own individual class - Method `mate` is overriden. + The method `mate` is overriden. """ ranking = None threshold = 0.25 @@ -217,12 +217,7 @@ class MyIndividual(MixIndividual[(_Chromosome,)*ndim + (uChromosome,)].set_fitne else: return super().mate(other) -class MyPopulation(StandardPopulation[MyIndividual]): - - def transition(self, *args, **kwargs): - self.sort() - super().transition(*args, **kwargs) - +MyPopulation = StandardPopulation[MyIndividual] ``` @@ -237,7 +232,7 @@ ax = fig.add_subplot(111) _Population = StandardPopulation[ExampleIndividual] pop = MyPopulation.random(n_individuals=20, sizes=[8]*ndim+[8]) -cpy = pop.clone(_Population) +cpy = pop.copy(type_=_Population) d = cpy.evolve(stat=stat, n_iter=100, history=True) ax.plot(d.index, d['Mean Fitness'], d.index, d['Best Fitness'], '.-') @@ -247,4 +242,198 @@ ax.legend(('Traditional mean','Traditional best', 'New mean', 'New best')) plt.show() ``` -![](comparison.png) \ No newline at end of file +![](comparison.png) + + +## Example 3 + +### Quantum GA + +It is based on quantum chromosomes. Let use have a look at the source code. + +```python +class QuantumChromosome(CircleChromosome): + + measure_result = None + + def decode(self): + self.measure() + return self.measure_result + + def measure(self): + # measure a QuantumChromosome to get a binary sequence + rs = np.random.random(size=(len(self),)) + self.measure_result = np.cos(self) ** 2 > rs + self.measure_result.astype(np.int_) +``` + +```python +#!/usr/bin/env python3 + +from pyrimidine import * +from pyrimidine.benchmarks.optimization import * + +from pyrimidine.deco import add_memory, fitness_cache + +# generate a knapsack problem randomly +n_bags = 50 +evaluate = Knapsack.random(n=n_bags) + +@fitness_cache +class YourIndividual(BinaryChromosome // n_bags): + + def _fitness(self): + return evaluate(self.decode()) + + +YourPopulation = HOFPopulation[YourIndividual] // 20 + +@fitness_cache +@add_memory({'measure_result': None, 'fitness': None}) +class MyIndividual(QuantumChromosome // n_bags): + + def _fitness(self): + return evaluate(self.decode()) + + def backup(self, check=False): + f = self._fitness() + if not check or (self.memory['fitness'] is None or f > self.memory['fitness']): + self._memory = { + 'measure_result': self.measure_result, + 'fitness': f + } + +class MyPopulation(HOFPopulation): + + element_class = MyIndividual + default_size = 20 + + def init(self): + self.backup() + super().init() + + def backup(self, check=True): + for i in self: + i.backup(check=check) + + def update_hall_of_fame(self, *args, **kwargs): + """ + Update the `hall_of_fame` after each step of evolution + """ + self.backup() + super().update_hall_of_fame(*args, **kwargs) + +``` + +### Visualization and comparison +```python +stat={'Mean Fitness': 'mean_fitness', 'Best Fitness': 'best_fitness'} +mypop = MyPopulation.random() + +yourpop = YourPopulation([YourIndividual(i.decode()) for i in mypop]) +mydata = mypop.evolve(n_iter=100, stat=stat, history=True) +yourdata = yourpop.evolve(n_iter=100, stat=stat, history=True) + +import matplotlib.pyplot as plt +fig = plt.figure() +ax = fig.add_subplot(111) +yourdata[['Mean Fitness', 'Best Fitness']].plot(ax=ax) +mydata[['Mean Fitness', 'Best Fitness']].plot(ax=ax) +ax.legend(('Mean Fitness', 'Best Fitness', 'Mean Fitness(Quantum)', 'Best Fitness(Quantum)')) +ax.set_xlabel('Generations') +ax.set_ylabel('Fitness') +ax.set_title(f'Demo of (Quantum)GA: {n_bags}-Knapsack Problem') +plt.show() + +``` + +![](QGA.png) + +## Game + +```python +#!/usr/bin/env python + + +from random import random, randint + +import numpy as np + +from pyrimidine import BasePopulation + + +class Player: + """ + 'scissors', 'paper', 'stone' = 0, 1, 2 + """ + + params = {'mutate_prob': 0.02} + + def __init__(self, strategy=0, score=0): + self.strategy = strategy # 1,2 + self.score = score + + @classmethod + def random(cls): + return cls(strategy=randint(0, 2), score=0) + + def clone(self, *args, **kwargs): + return self.__class__(self.strategy, self.score) + + def mutate(self): + self.strategy = randint(0, 2) + + def init(self): + pass + + def __lt__(self, other): + return ((self.strategy, other.strategy) == (0, 1) + or (self.strategy, other.strategy) == (1, 2) + or (self.strategy, other.strategy) == (2, 0)) + + +class Game(BasePopulation): + + element_class = Player + default_size = 100 + + def transition(self, *args, **kwargs): + self.compete() + self.duplicate() + self.mutate() + + def compete(self): + k = int(0.5 * self.default_size) + winner = [] + for i, p in enumerate(self[:-1]): + for j, q in enumerate(self[:i]): + if random() < 0.5: + if p < q: + p.score += 1 + q.score -= 1 + elif q < p: + p.score -= 1 + q.score += 1 + winners = np.argsort([p.score for p in self])[-k:] + self.elements = [self.elements[k] for k in winners] + + def duplicate(self): + self.extend(self.clone()) + + +game = Game.random() +stat = {'scissors': lambda game: sum(p.strategy==0 for p in game), +'paper': lambda game: sum(p.strategy==1 for p in game), +'stone': lambda game: sum(p.strategy==2 for p in game) +} +data = game.evolve(stat=stat, history=True) + +import matplotlib.pyplot as plt +fig = plt.figure() +ax = fig.add_subplot(111) +data[['scissors', 'paper', 'stone']].plot(ax=ax) +ax.set_title("Have a zero-sum game") +plt.show() +``` + +![](game.png) \ No newline at end of file diff --git a/docs/build/_sources/source/Helpers.md.txt b/docs/build/_sources/source/Helpers.md.txt index 8f5b494..b775a8c 100644 --- a/docs/build/_sources/source/Helpers.md.txt +++ b/docs/build/_sources/source/Helpers.md.txt @@ -20,3 +20,97 @@ print(solution) ``` OUTPUT: `[-0.0078125 -1. ]` + +## Decorators + + +### Memory +```python +#!/usr/bin/env python3 + +from pyrimidine import * +from pyrimidine.benchmarks.optimization import * + +from pyrimidine.deco import add_memory + +# generate a knapsack problem randomly +n_bags = 50 +evaluate = Knapsack.random(n_bags) + +class YourIndividual(BinaryChromosome // n_bags): + + def _fitness(self): + return evaluate(self.decode()) + + +YourPopulation = HOFPopulation[YourIndividual] // 20 + + +@add_memory({'solution': None, 'fitness': None}) +class MyIndividual(YourIndividual): + # Individual with a memory, recording a best solution + + def backup(self, check=False): + f = self._fitness() + if not check or (self.memory['fitness'] is None or f > self.memory['fitness']): + self._memory = { + 'solution': self.clone(), + 'fitness': f + } + + @property + def solution(self): + if self._memory['solution'] is not None: + return self._memory['solution'] + else: + return self.solution + + +class MyPopulation(HOFPopulation): + + element_class = MyIndividual + default_size = 20 + + def init(self): + self.backup() + super().init() + + def backup(self, check=True): + for i in self: + i.backup(check=check) + + def transition(self, *args, **kwargs): + """ + Update the `hall_of_fame` after each step of evolution + """ + self.backup() + super().transition(*args, **kwargs) + + +stat = {'Mean Fitness': 'mean_fitness', 'Best Fitness': 'best_fitness'} +mypop = MyPopulation.random() + +yourpop = mypop.clone(type_=YourPopulation) +mydata = mypop.evolve(n_iter=200, stat=stat, history=True) +yourdata = yourpop.evolve(n_iter=200, stat=stat, history=True) + +import matplotlib.pyplot as plt +fig = plt.figure() +ax = fig.add_subplot(111) +yourdata[['Mean Fitness', 'Best Fitness']].plot(ax=ax) +mydata[['Mean Fitness', 'Best Fitness']].plot(ax=ax) +ax.legend(('Mean Fitness', 'Best Fitness', 'Mean Fitness(Memory)', 'Best Fitness(Memory)')) +ax.set_xlabel('Generations') +ax.set_ylabel('Fitness') +ax.set_title(f'Demo of GA: {n_bags}-Knapsack Problem') +plt.show() + +``` + +### Cache +Cache the fitness, if the indiviudal dose not change, the fitness will be read from cache by default. + +```python +@fitness_cache +class MyIndividual... +``` \ No newline at end of file diff --git a/docs/build/_sources/source/Install.md.txt b/docs/build/_sources/source/Install.md.txt index 7e3f095..ce6048a 100644 --- a/docs/build/_sources/source/Install.md.txt +++ b/docs/build/_sources/source/Install.md.txt @@ -1,6 +1,6 @@ # Installation and Getting Started -**Pythons**: Python 3.7, 3.8, 3.9, 3.10, PyPy3 +**Pythons**: Python 3.8, 3.9, 3.10, 3.11, PyPy3 **Platforms**: Linux/Unix and Windows diff --git a/docs/build/_sources/source/Misc.md.txt b/docs/build/_sources/source/Misc.md.txt index 58671df..eb37ba0 100644 --- a/docs/build/_sources/source/Misc.md.txt +++ b/docs/build/_sources/source/Misc.md.txt @@ -1,8 +1,19 @@ -# More Algorithms +# Misc +## Pictures + + +## Videos + + \ No newline at end of file diff --git a/docs/build/genindex.html b/docs/build/genindex.html index a5bacd2..dcaf5ce 100644 --- a/docs/build/genindex.html +++ b/docs/build/genindex.html @@ -71,9 +71,7 @@

A

@@ -173,16 +169,16 @@

A

  • alpine() (in module pyrimidine.benchmarks.special)
  • -
  • apply() (pyrimidine.base.BaseIndividual method) +
  • apply() (pyrimidine.base.BaseCommunity method)
  • argsort() (pyrimidine.benchmarks.optimization.Knapsack method) + +
  • ArrayChromosome (class in pyrimidine.chromosome)
  • @@ -250,6 +246,8 @@

    B

  • BaseChromosome (class in pyrimidine.base) +
  • +
  • BaseCommunity (class in pyrimidine.base)
  • BaseEnvironment (class in pyrimidine.base)
  • @@ -264,34 +262,32 @@

    B

  • BaseParticle (class in pyrimidine.pso)
  • BasePopulation (class in pyrimidine.base) -
  • -
  • BaseProblem (class in pyrimidine.benchmarks)