https://github.com/primaryobjects/genetic-programming
Genetic programming for math equations with prefix expression trees.
https://github.com/primaryobjects/genetic-programming
ai artificial artificial-intelligence evolution evolutionary-algorithms genetic-algorithm genetic-programming infix-notation intelligence javascript nodejs prefix-notation prefix-tree
Last synced: 9 months ago
JSON representation
Genetic programming for math equations with prefix expression trees.
- Host: GitHub
- URL: https://github.com/primaryobjects/genetic-programming
- Owner: primaryobjects
- Created: 2018-08-02T19:23:24.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2018-09-02T19:29:54.000Z (over 7 years ago)
- Last Synced: 2025-03-21T06:41:37.646Z (10 months ago)
- Topics: ai, artificial, artificial-intelligence, evolution, evolutionary-algorithms, genetic-algorithm, genetic-programming, infix-notation, intelligence, javascript, nodejs, prefix-notation, prefix-tree
- Language: JavaScript
- Size: 198 KB
- Stars: 17
- Watchers: 4
- Forks: 5
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
Genetic Programming
-------------------
Read the full article at:
[Creating Self-Assembling Code with Genetic Programming](http://www.primaryobjects.com/2018/09/03/creating-self-assembling-code-with-genetic-programming/)
Genetic programming is a branch of artificial intelligence that uses evolutionary computation to generate computer programs for solving a particular task. The computer programs are represented by encoded genes and slightly modified at each time-step via cross-over and mutation until a resulting program is found that solves the given task to a degree of accuracy.
This project hosts a demo of several genetic algorithm and genetic programming examples written in JavaScript with the [genetic-js](https://github.com/subprotocol/genetic-js) library.
- Hello World
*A traditional "hello world" example for genetic algorithms, where the goal is to output the text, "Hello World".*

- Prefix
*A genetic programming example where a computer program is evolved to represent a specific mathematical expression, in prefix notation format.*

- Variable
*A genetic programming example where a computer program is evolved to represent a mathematical expression containing both numbers and variables (i.e., formulas ) in prefix notation format.*

## Genetic Algorithms in JavaScript
The [genetic-js](https://github.com/subprotocol/genetic-js) library facilitates the construction of a genetic algorithm in JavaScript. It provides the necessary functions and handlers for defining a genetic algorithm, its initialization, a fitness function, and a means for ranking solutions. In this manner, a genetic algorithm can be developed for a large variety of tasks, and as shown in this project, genetic programming.
## Initialization
Usage of the genetic algorithm library in node.js is relatively straight-forward. You can begin by instantiating the genetic class and selecting an optimization method and selection algorithm.
```js
const Genetic = require('genetic-js');
const genetic = Genetic.create();
genetic.optimize = Genetic.Optimize.Maximize;
genetic.select1 = Genetic.Select1.Tournament2;
genetic.select2 = Genetic.Select2.Tournament2;
```
In the above example, we've instantiated the genetic class and selected to maximize the fitness among the pool of genomes. The best performing results will continue on to the next epoch of evolution.
## Creating the Initial Population
After creating the initial genetic algorithm class, you can provide a function of creating the initial pool of genomes to begin evolution from. This set of genomes is usually randomly produced, with some of the genomes performing better than others for solving the desired task. It's these better performing genomes that will rank high and move on to the next epoch of evolution, creating the next generation.
```js
genetic.seed = function() {
// Return a random string for this particular genome.
return 'abc';
}
```
## Crossover
When creating the next population of genomes after an epoch, the methods of crossover and mutation are used to slightly modify the existing genomes and create children. Crossover is the process of creating a child genome by taking two parent genomes and mating them together to produce offspring. This is done by taking a series of genes from parent 1 and parent 2 and combining them together to form a child. Additional mutation can be used on the child to slightly tweak the genes further, which has the potential to produce beneficial (or worse) functionality for the child.
A simplified example for performing crossover on two parent genomes to produce two child genomes is shown below.
```js
genetic.crossover = function(parent1, parent2) {
let index = Math.floor(Math.random() * parent1.length);
const parent1Left = parent1.substr(0, index);
const parent1Right = parent1.substr(index);
const parent2Left = parent2.substr(0, index);
const parent2Right = parent2.substr(index);
// Crossover the left and right side.
let child1 = parent1Left + parent2Right;
let child2 = parent2Left + parent1Right;
return [child1, child2];
}
```
In the above code, we're simply splitting the two parent genomes at a specific index. We then take the left portion of one parent and combine it with the right portion of the other parent to produce a child offspring. We repeat this a second time for the second child offspring, by taking the right portion of the first parent and combine it with the left portion of the second parent.
## Mutation
After performing crossover, each child genome can be slightly modified a bit further by mutating specific genes. This may or may not produce beneficial effects that can boost the child's resulting fitness score. Mutation can be performed by randomly altering the value of a gene or by inserting and removing genes at specific indices within the genome.
```js
genetic.mutate = function(entity) {
const replaceAt = (str, index, replacement) => {
return str.substr(0, index) + replacement + str.substr(index + replacement.length);
};
let index = Math.floor(Math.random() * entity.length);
// Mutate the instruction up or down by 1 place (according to the direction).
const direction = Math.floor(Math.random() * 2);
return replaceAt(entity, index, String.fromCharCode(entity.charCodeAt(index) + (direction ? 1 : -1)));
}
```
In the above code, we're performing mutation by replacing a randomly selected single gene within the child. We modify the gene by either incrementing or decrementing it by a value of 1. In the case of letters representing the genes, this would select either the previous or next letter within the alphabet. For numbers, this may add or subtract some fraction from the original value.
## Fitness
Scoring the fitness for a genome is the most important task of developing a genetic algorithm, as this contains the core logic for guiding which genomes are to be selected for producing the next generation of genomes and thus, solving a particular task. A granular method of scoring the fitness for each genome is required in order to allow the genetic algorithm to rank genomes accordingly, even if they don't solve the entire given task. By assigning partial credit to those genomes which perform just slightly better than others, the evolution process can select better performing programs in a granular fashion until it arrives at a solution.
```js
genetic.fitness = function(entity) {
let score = 0;
for (let i=0; i