-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Prettify README and use js syntax for the example
- Loading branch information
Showing
1 changed file
with
120 additions
and
107 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,141 +1,154 @@ | ||
|
||
# genome.js | ||
|
||
*genome.js* is a Javascript to help build insane genetics algorithms in a few minutes. | ||
|
||
|
||
_genome.js_ is a Javascript to help build insane genetics algorithms in a few minutes. | ||
|
||
## Concept | ||
|
||
### General terms | ||
|
||
- **Population**: a subset of the possible solutions to the problem (ie. subset of *chromosomes*) | ||
|
||
- **Chromosome**: a specific solution to the problem | ||
- **Population**: a subset of the possible solutions to the problem (ie. subset of _chromosomes_) | ||
|
||
- **Gene**: a value defining a chromosome | ||
- **Chromosome**: a specific solution to the problem | ||
|
||
- **Gene**: a value defining a chromosome | ||
|
||
### Specific terms | ||
|
||
- **Blueprint**: a schema defining the structure of every *gene* (number and possible values) in a *chromosome*. | ||
- **Blueprint**: a schema defining the structure of every _gene_ (number and possible values) in a _chromosome_. | ||
|
||
## Installation (via NPM) | ||
|
||
|
||
|
||
```npm install --save genome.js``` | ||
|
||
|
||
`npm install --save genome.js` | ||
|
||
## Documentation | ||
|
||
### Population | ||
|Methods|Return type|Description| | ||
|--|--|--| | ||
| `constructor(size: number, blueprint: Blueprint)` |`Population`|Create a population with *`size`* chromosomes using the *`blueprint`*| | ||
|`setFitnessCalculation(fitnessCalculation: any)`|`null`|Set the fitness calculation function. It should return a `number` value corresponding to the fitness of a chromosome.| | ||
| `setStopAt(fitness: number)` |`null`|Stop the process once a chromosome reaches **AT LEAST** *`fitness`* value on its fitness.| | ||
| `setMutationRate(mutationRate: number)` |`null`|Set the mutation rate value. It should be between `0` *(no mutation at all)* and `1` *(every chromosome will be mutated)*| | ||
| `setCutOff(cutOff: number)` |`null`|Set the cut off value. It should be between `0` *(no chromosome will be removed)* and `1` *(every chromosome will be removed)*| | ||
| `run(rounds: number = 1)` |`null`|Run the process *`rounds`* times.| | ||
| `getGenerationNumber()` |`number`|Return the current round number.| | ||
| `getBestChromosome()` |`Chromosome`|Return the best chromosome.| | ||
|
||
| Methods | Return type | Description | | ||
| ------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------ | | ||
| `constructor(size: number, blueprint: Blueprint)` | `Population` | Create a population with _`size`_ chromosomes using the _`blueprint`_ | | ||
| `setFitnessCalculation(fitnessCalculation: any)` | `null` | Set the fitness calculation function. It should return a `number` value corresponding to the fitness of a chromosome. | | ||
| `setStopAt(fitness: number)` | `null` | Stop the process once a chromosome reaches **AT LEAST** _`fitness`_ value on its fitness. | | ||
| `setMutationRate(mutationRate: number)` | `null` | Set the mutation rate value. It should be between `0` _(no mutation at all)_ and `1` _(every chromosome will be mutated)_ | | ||
| `setCutOff(cutOff: number)` | `null` | Set the cut off value. It should be between `0` _(no chromosome will be removed)_ and `1` _(every chromosome will be removed)_ | | ||
| `run(rounds: number = 1)` | `null` | Run the process _`rounds`_ times. | | ||
| `getGenerationNumber()` | `number` | Return the current round number. | | ||
| `getBestChromosome()` | `Chromosome` | Return the best chromosome. | | ||
|
||
### Chromosome | ||
|Methods|Return type|Description| | ||
|--|--|--| | ||
| `getGenes()` |`Gene[]`|Return the genes of the chromosome.| | ||
| `getFitness()` |`Gene[]`|Return the fitness of the chromosome.| | ||
|
||
| Methods | Return type | Description | | ||
| -------------- | ----------- | ------------------------------------- | | ||
| `getGenes()` | `Gene[]` | Return the genes of the chromosome. | | ||
| `getFitness()` | `Gene[]` | Return the fitness of the chromosome. | | ||
|
||
### Gene | ||
|Methods|Return type|Description| | ||
|--|--|--| | ||
| `get()` |`number`|Return the allele (value) of the gene.| | ||
|
||
| Methods | Return type | Description | | ||
| ------- | ----------- | -------------------------------------- | | ||
| `get()` | `number` | Return the allele (value) of the gene. | | ||
|
||
### Blueprint | ||
|Methods|Return type|Description| | ||
|--|--|--| | ||
| `constructor()` |`Blueprint`|Create a new *`Blueprint`*.| | ||
| `add(factor: number, times: number = 1)` |`null`|Define a property into the blueprint. The *`factor`* is used when you get back the allele (value) of a gene *(ex: a gene created with `add(26)` will return a `number` between `0` and `25`)*. You can add *`times`* a property by setting the *`times`* parameter.| | ||
|
||
| Methods | Return type | Description | | ||
| ---------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| `constructor()` | `Blueprint` | Create a new _`Blueprint`_. | | ||
| `add(factor: number, times: number = 1)` | `null` | Define a property into the blueprint. The _`factor`_ is used when you get back the allele (value) of a gene _(ex: a gene created with `add(26)` will return a `number` between `0` and `25`)_. You can add _`times`_ a property by setting the _`times`_ parameter. | | ||
|
||
### GenoveEvent | ||
|Methods|Return type|Description| | ||
|--|--|--| | ||
| `static on(eventType: GenomeEventType, callback: any)` |`null`|***STATIC*** Run the `callback` function when the event `eventType` is trigger.| | ||
|
||
| Methods | Return type | Description | | ||
| ------------------------------------------------------ | ----------- | ------------------------------------------------------------------------------- | | ||
| `static on(eventType: GenomeEventType, callback: any)` | `null` | **_STATIC_** Run the `callback` function when the event `eventType` is trigger. | | ||
|
||
## Events | ||
|
||
|Name|Description| | ||
|--|--| | ||
|`GENOME_EVENT_POPULATION_CREATED`|Trigger when all chromosomes are initialized| | ||
|`GENOME_EVENT_GENERATION_BEGIN`|Trigger when a new generation is processed| | ||
|`GENOME_EVENT_GENERATION_END`|Trigger when a generation is done processing| | ||
|`GENOME_EVENT_GENERATION_FINISH`|Trigger when the all processing is done *(rounds limit or fitness limit)*| | ||
| Name | Description | | ||
| --------------------------------- | ------------------------------------------------------------------------- | | ||
| `GENOME_EVENT_POPULATION_CREATED` | Trigger when all chromosomes are initialized | | ||
| `GENOME_EVENT_GENERATION_BEGIN` | Trigger when a new generation is processed | | ||
| `GENOME_EVENT_GENERATION_END` | Trigger when a generation is done processing | | ||
| `GENOME_EVENT_GENERATION_FINISH` | Trigger when the all processing is done _(rounds limit or fitness limit)_ | | ||
|
||
## Example | ||
|
||
/* | ||
* This example is based on the "infinite monkey theorem" (https://en.wikipedia.org/wiki/Infinite_monkey_theorem) | ||
* | ||
* The algorithm tries to reproduce a specific text input, here "helloworldhowareyoutoday" in a minimum rounds. | ||
*/ | ||
|
||
// Importing all the dependencies | ||
import { Population, Blueprint, Gene, Chromosome, GenomeEvent, GenomeEventType } from 'genome.js'; | ||
|
||
// Defining the string to reproduce | ||
const answer = 'helloworldhowareyoutoday'; | ||
|
||
// We create a blueprint to represent the data structure of a chromosome | ||
const blueprint = new Blueprint(); | ||
// Our chromosomes will have 'answer.length' genes between 0 and 26 (not included), so that each gene can represent one letter of the alphabet | ||
blueprint.add(26, answer.length); | ||
|
||
// We generate a population of 500 chromosomes using our blueprint | ||
const population = new Population(500, blueprint); | ||
|
||
// Just some basic configurations | ||
population.setMutationRate(0.01); | ||
population.setCutOff(0.5); | ||
population.setStopAt(100); // We stop the processing when a chromosome reach AT LEAST 100 on his fitness | ||
|
||
// We define now the function that calculate the fitness of every chromosome on each generation | ||
// Be sure to never return 0 (cause a bug, WIP) | ||
population.setFitnessCalculation((genes: Gene[]) => { | ||
let sum = 1; // Avoid to have 0 on fitness | ||
|
||
for (let i = 0; i < genes.length; i += 1) { | ||
const charCode = answer.charCodeAt(i) - 97; | ||
const geneCharCode = Math.floor(genes[i].get()); | ||
// If the gene value is corresponding with the answer letter at the same location, then increment 'sum' | ||
if (charCode === geneCharCode) { | ||
sum += 1; | ||
} | ||
} | ||
|
||
// Basically a percent of correct genes' values | ||
return (sum / (genes.length + 1)) * 100; | ||
}); | ||
|
||
// We wait for a generation to end, and we display the best chromosome fitness into the console | ||
GenomeEvent.on(GenomeEventType.GENOME_EVENT_GENERATION_END, (chromosomes: Chromosome[]) => { | ||
const bestChromosome = chromosomes[0]; | ||
console.log(`Generation ${population.getGenerationNumber()}: ${bestChromosome.getFitness()}`); | ||
}); | ||
|
||
// Once the process in finished (when a chromosome reach the fitness limit or the process has reach the round limit), we display the string contained in its genes | ||
GenomeEvent.on(GenomeEventType.GENOME_EVENT_GENERATION_FINISH, (chromosomes: Chromosome[]) => { | ||
let finalString = ''; | ||
const bestChromosome = chromosomes[0]; | ||
bestChromosome.getGenes().map((gene: Gene) => { | ||
finalString += String.fromCharCode(gene.get() + 97); | ||
}); | ||
console.log(`Result (fitness: ${bestChromosome.getFitness()}): ${finalString}`); | ||
}); | ||
|
||
// We process the algorithm throught 500 rounds (more options comming soon) | ||
population.run(500); | ||
|
||
```js | ||
/* | ||
* This example is based on the "infinite monkey theorem" (https://en.wikipedia.org/wiki/Infinite_monkey_theorem) | ||
* | ||
* The algorithm tries to reproduce a specific text input, here "helloworldhowareyoutoday" in a minimum rounds. | ||
*/ | ||
|
||
// Importing all the dependencies | ||
import { | ||
Population, | ||
Blueprint, | ||
Gene, | ||
Chromosome, | ||
GenomeEvent, | ||
GenomeEventType | ||
} from "genome.js"; | ||
|
||
// Defining the string to reproduce | ||
const answer = "helloworldhowareyoutoday"; | ||
|
||
// We create a blueprint to represent the data structure of a chromosome | ||
const blueprint = new Blueprint(); | ||
// Our chromosomes will have 'answer.length' genes between 0 and 26 (not included), so that each gene can represent one letter of the alphabet | ||
blueprint.add(26, answer.length); | ||
|
||
// We generate a population of 500 chromosomes using our blueprint | ||
const population = new Population(500, blueprint); | ||
|
||
// Just some basic configurations | ||
population.setMutationRate(0.01); | ||
population.setCutOff(0.5); | ||
population.setStopAt(100); // We stop the processing when a chromosome reach AT LEAST 100 on his fitness | ||
|
||
// We define now the function that calculate the fitness of every chromosome on each generation | ||
// Be sure to never return 0 (cause a bug, WIP) | ||
population.setFitnessCalculation((genes: Gene[]) => { | ||
let sum = 1; // Avoid to have 0 on fitness | ||
|
||
for (let i = 0; i < genes.length; i += 1) { | ||
const charCode = answer.charCodeAt(i) - 97; | ||
const geneCharCode = Math.floor(genes[i].get()); | ||
// If the gene value is corresponding with the answer letter at the same location, then increment 'sum' | ||
if (charCode === geneCharCode) { | ||
sum += 1; | ||
} | ||
} | ||
|
||
// Basically a percent of correct genes' values | ||
return (sum / (genes.length + 1)) * 100; | ||
}); | ||
|
||
// We wait for a generation to end, and we display the best chromosome fitness into the console | ||
GenomeEvent.on( | ||
GenomeEventType.GENOME_EVENT_GENERATION_END, | ||
(chromosomes: Chromosome[]) => { | ||
const bestChromosome = chromosomes[0]; | ||
console.log( | ||
`Generation ${population.getGenerationNumber()}: ${bestChromosome.getFitness()}` | ||
); | ||
} | ||
); | ||
|
||
// Once the process in finished (when a chromosome reach the fitness limit or the process has reach the round limit), we display the string contained in its genes | ||
GenomeEvent.on( | ||
GenomeEventType.GENOME_EVENT_GENERATION_FINISH, | ||
(chromosomes: Chromosome[]) => { | ||
let finalString = ""; | ||
const bestChromosome = chromosomes[0]; | ||
bestChromosome.getGenes().map((gene: Gene) => { | ||
finalString += String.fromCharCode(gene.get() + 97); | ||
}); | ||
console.log( | ||
`Result (fitness: ${bestChromosome.getFitness()}): ${finalString}` | ||
); | ||
} | ||
); | ||
|
||
// We process the algorithm throught 500 rounds (more options comming soon) | ||
population.run(500); | ||
``` |