diff options
Diffstat (limited to 'src/genome')
-rw-r--r-- | src/genome/README.md | 12 | ||||
-rw-r--r-- | src/genome/genome.js | 218 | ||||
-rw-r--r-- | src/genome/genome.test.js | 282 | ||||
-rw-r--r-- | src/genome/trial.js | 37 |
4 files changed, 0 insertions, 549 deletions
diff --git a/src/genome/README.md b/src/genome/README.md deleted file mode 100644 index 1135ed0..0000000 --- a/src/genome/README.md +++ /dev/null @@ -1,12 +0,0 @@ -src/genome
-==========
-
-Genomes represent the neural network that underlies a creature. They are an array of tuples of the form `[ source, sink, weight ]`, where:
-
- * `source` is an integer in the range `[0, N - num_outputs)`, representing the index of the source neuron
- * `sink` is an integer in the range `[num_inputs, N)`, representing the index of the sink neuron
- * `weight` is a floating-point value in the range `[-4, 4]`, representing the weight of the connection
-
-`num_input` and `num_output` are fixed by the environment (as they are the input senses and output actions of the creature, respectively).
-`N` is not fixed, but is instead determined by the maximum index present in the genome. As long as the maximum index is greater than or
-equal to `num_input + num_output`, the genome is considered valid.
diff --git a/src/genome/genome.js b/src/genome/genome.js deleted file mode 100644 index 20974fc..0000000 --- a/src/genome/genome.js +++ /dev/null @@ -1,218 +0,0 @@ -'use strict';
-
-import { random_choice } from '../util.js';
-import { network } from '../mind/topology.js';
-
-
-// check if a given genome is valid and compute its size
-export function validate_genome(genome) {
- const { n_input, n_internal, n_output } = genome;
- console.log(n_input + n_internal);
- return genome.genes.reduce(
- (acc, [source, sink, weight]) => acc && (
- (source < n_input+n_internal) &&
- (sink >= n_input)
- ),
- true
- );
-}
-
-// parse a genome into a useable neural net
-export function parse_genome(genome) {
- const { n_input, n_internal, n_output } = genome;
-
- const n = genome.genes.reduce(
- (acc, [source, sink, weight]) => acc.connect(source, sink, weight),
- network(n_input, n_internal, n_output)
- );
-
- return n;
-}
-
-
-// --===== mutations =====--
-
-function clamp(value, min, max) {
- if (value > max) { return max; }
- if (value < min) { return min; }
- return value;
-}
-
-// adjust the source input of a gene
-export function mut_gene_source(n_input, n_internal, n_output, gene, r) {
- const [source, sink, weight] = gene;
-
- const new_source = r < 0.5 ? source-1 : source+1;
-
- return [
- clamp(new_source, 0, n_input+n_internal-1),
- sink,
- weight,
- ];
-}
-
-
-// adjust the sink target of a gene
-export function mut_gene_sink(n_input, n_internal, n_output, gene, r) {
- const [source, sink, weight] = gene;
-
- const new_sink = r < 0.5 ? sink-1 : sink+1;
-
- return [
- source,
- clamp(new_sink, n_input+n_internal, n_input+n_internal+n_output-1),
- weight,
- ];
-}
-
-
-// modify a gene's weight
-// only adjusts the weight by performing a weighted average, so as to
-// more gently modify the generated net
-export function mut_gene_weight(weight_max, gene, r) {
- const [source, sink, weight] = gene;
-
- const rr = (2*r)-1;
- const move = weight_max * rr;
- const new_weight = (2*weight + move)/3;
-
- return [
- source,
- sink,
- clamp(new_weight, -weight_max, weight_max),
- ];
-}
-
-
-
-// expand the size of the neural net encoded by the genome
-// relabels internal indices so that there is one extra internal neuron
-export function mut_genome_expand(genome, r) {
- const expand_index = Math.floor(genome.n_internal * r) + genome.n_input;
- const new_genes = genome.genes.map(([source, sink, weight]) => [
- source >= expand_index ? source+1 : source,
- sink >= expand_index ? sink+1 : sink,
- weight,
- ]);
-
- return {
- ...genome,
- n_internal: genome.n_internal+1,
- genes: new_genes,
- };
-}
-
-
-// contract the size of the neural net encoded by the genome
-// relabels internal indices so that there is one less internal neuron
-export function mut_genome_contract(genome, r) {
- const { n_input, n_internal, n_output } = genome;
- const contract_idx = Math.floor(n_internal * r) + n_input;
-
- // decrement sources on the contract index too, to prevent invalid genomes
- const new_source = (source) => source >= contract_idx ? source-1 : source;
- // decrement sinks only after the contract index
- const new_sink = (sink) => sink > contract_idx ? sink-1 : sink;
-
- const new_genes = genome.genes.map(([source, sink, weight]) => [
- new_source(source),
- new_sink(sink),
- weight,
- ]);
-
- return {
- ...genome,
- n_internal: n_internal-1,
- genes: new_genes,
- };
-}
-
-
-// append a newly generated gene to the end of the genome
-export function mut_genome_insert(genome, weight_max, r1=Math.random(), r2=Math.random(), r3=Math.random()) {
- const { n_input, n_internal, n_output } = genome;
- const source = Math.floor((n_input + n_internal) * r1);
- const sink = Math.floor((n_internal + n_output) * r2) + n_input;
- const weight = weight_max * ((2*r3)-1);
-
- return {
- ...genome,
- genes: [...genome.genes, [source, sink, weight]],
- };
-}
-
-
-// delete a gene from the genome
-export function mut_genome_delete(genome, r) {
- const del_idx = Math.floor(r * genome.genes.length);
- const genes = genome.genes.filter((_, idx) => idx != del_idx);
- return { ...genome, genes };
-}
-
-
-function mut_gene(
- [n_input, n_internal, n_output, genome],
- weight_max, r1, r2, r3
-) {
- const gene_idx = Math.floor(genome.length * r1);
- const mod = random_choice(['source', 'sink', 'weight'], r2);
- let new_gene;
- if (mod == 'source') {
- new_gene = mut_gene_source(
- n_input, n_internal, n_output,
- genome[gene_idx],
- r3
- );
- } else if (mod == 'sink') {
- new_gene = mut_gene_sink(
- n_input, n_internal, n_output,
- genome[gene_idx],
- r3
- );
- } else {
- new_gene = mut_gene_weight(
- weight_max, genome[gene_idx], r3
- );
- }
-
- const new_genome = genome.map((gene, idx) => {
- if (idx == gene_idx) { return new_gene; }
- return gene;
- });
-
- return [
- n_input, n_internal, n_output, new_genome
- ];
-}
-
-
-export function mutate_genome(obj, weight_max) {
- const mut = random_choice([
- 'gene', 'gene', 'gene',
- 'gene', 'gene', 'gene',
- 'gene', 'gene', 'gene',
- 'insert', 'delete',
- 'insert', 'delete',
- 'expand', 'contract',
- ], Math.random());
-
- if (mut == 'gene') {
- return mut_gene(
- obj, weight_max,
- Math.random(), Math.random(), Math.random()
- );
- } else if (mut == 'insert') {
- return mut_genome_insert(
- obj, weight_max,
- Math.random(), Math.random(), Math.random()
- );
- } else if (mut == 'delete') {
- return mut_genome_delete(obj, Math.random());
- } else if (mut == 'expand') {
- return mut_genome_expand(obj, Math.random());
- } else if (mut == 'contract') {
- return mut_genome_contract(obj, Math.random());
- } else {
- throw new Error(`bad mut value: ${mut}`);
- }
-}
diff --git a/src/genome/genome.test.js b/src/genome/genome.test.js deleted file mode 100644 index bc64e4e..0000000 --- a/src/genome/genome.test.js +++ /dev/null @@ -1,282 +0,0 @@ -'use strict';
-
-// genome structure
-// {
-// genes: gene[]
-// n_input, n_internal, n_output
-// }
-
-
-import {
- validate_genome,
- parse_genome,
- mut_gene_source,
- mut_gene_sink,
- mut_gene_weight,
- mut_genome_expand,
- mut_genome_contract,
- mut_genome_insert,
- mut_genome_delete,
-} from './genome';
-
-
-test('genome validation', () => {
- expect(validate_genome({
- n_input: 0, n_internal: 1, n_output: 0,
- genes: [[0, 0, 1.0]],
- })).toBe(true);
- expect(validate_genome({
- n_input: 2, n_internal: 0, n_output: 1,
- genes: [[0, 2, 1]],
- })).toBe(true);
- expect(validate_genome({
- n_input: 2, n_internal: 0, n_output: 1,
- genes: [[2, 0, 1]],
- })).toBe(false);
- expect(validate_genome({
- n_input: 2, n_internal: 0, n_output: 1,
- genes: [[2, 2, 1]],
- })).toBe(false);
- expect(validate_genome({
- n_input: 2, n_internal: 1, n_output: 1,
- genes: [[3, 2, 1]],
- })).toBe(false);
-});
-
-
-test('parse a genome into a neural net', () => {
- const n = parse_genome({
- n_input: 1, n_internal: 1, n_output: 1,
- genes: [
- [0, 1, 1],
- [1, 1, 1],
- [1, 2, 1]
- ]
- });
-
- expect(n.input_count).toBe(1);
- expect(n.output_count).toBe(1);
- expect(n.compute([2], [-1])).toEqual([
- [ Math.tanh( Math.tanh( 2-1 ) ) ],
- [ Math.tanh( 2-1 ) ],
- ]);
-});
-
-
-test('mutate gene source', () => {
- const n_input = 3;
- const n_internal = 4;
- const n_output = 5;
-
- expect(mut_gene_source(
- n_input, n_internal, n_output,
- [0, 4, 0],
- 0.0
- )).toEqual([0, 4, 0]);
-
- expect(mut_gene_source(
- n_input, n_internal, n_output,
- [0, 4, 0],
- 1.0
- )).toEqual([1, 4, 0]);
-
- expect(mut_gene_source(
- n_input, n_internal, n_output,
- [6, 4, 0],
- 0.0
- )).toEqual([5, 4, 0]);
-
- expect(mut_gene_source(
- n_input, n_internal, n_output,
- [6, 4, 0],
- 1.0
- )).toEqual([6, 4, 0]);
-});
-
-
-test('mutate gene sink', () => {
- const n_input = 3;
- const n_internal = 4;
- const n_output = 5;
-
- expect(mut_gene_sink(
- n_input, n_internal, n_output,
- [0, 7, 0],
- 0.0
- )).toEqual([0, 7, 0]);
-
- expect(mut_gene_sink(
- n_input, n_internal, n_output,
- [0, 7, 0],
- 1.0
- )).toEqual([0, 8, 0]);
-
- expect(mut_gene_sink(
- n_input, n_internal, n_output,
- [6, 11, 0],
- 0.0
- )).toEqual([6, 10, 0]);
-
- expect(mut_gene_sink(
- n_input, n_internal, n_output,
- [6, 11, 0],
- 1.0
- )).toEqual([6, 11, 0]);
-});
-
-
-test('mutate gene weight', () => {
- const weight_max = 4.0;
-
- expect(mut_gene_weight(
- weight_max, [0, 0, 1], 0.0
- )).toEqual([0, 0, (2 - 4)/3]);
-
- expect(mut_gene_weight(
- weight_max, [0, 0, -4], 1.0
- )).toEqual([0, 0, (-8 + 4)/3]);
-
- expect(mut_gene_weight(
- weight_max, [0, 0, 3], 0.5
- )).toEqual([0, 0, (6+0)/3]);
-});
-
-
-test('expand genome', () => {
- const n_input = 1;
- const n_internal = 3;
- const n_output = 1;
-
- const genome = {
- n_input, n_internal, n_output,
- genes: [
- [0, 1, 0],
- [1, 2, 0],
- [2, 3, 0],
- [3, 4, 0],
- ],
- };
-
- expect(mut_genome_expand(genome, 0.0)).toEqual({
- n_input, n_internal: n_internal+1, n_output,
- genes: [
- [0, 2, 0],
- [2, 3, 0],
- [3, 4, 0],
- [4, 5, 0],
- ],
- });
-
- expect(mut_genome_expand(genome, 0.5)).toEqual({
- n_input, n_internal: n_internal+1, n_output,
- genes: [
- [0, 1, 0],
- [1, 3, 0],
- [3, 4, 0],
- [4, 5, 0],
- ],
- });
-
- expect(mut_genome_expand(genome, 0.99)).toEqual({
- n_input, n_internal: n_internal+1, n_output,
- genes: [
- [0, 1, 0],
- [1, 2, 0],
- [2, 4, 0],
- [4, 5, 0],
- ],
- });
-});
-
-
-test('contract genome', () => {
- const n_input = 1;
- const n_internal = 3;
- const n_output = 1;
-
- const genome = {
- n_input, n_internal, n_output,
- genes: [
- [0, 1, 0],
- [1, 2, 1],
- [2, 3, 2],
- [3, 4, 3],
- ],
- };
-
- expect(mut_genome_contract(genome, 0.0)).toEqual({
- n_input, n_internal: n_internal-1, n_output,
- genes: [
- [0, 1, 0],
- [0, 1, 1],
- [1, 2, 2],
- [2, 3, 3],
- ],
- });
-
- expect(mut_genome_contract(genome, 0.5)).toEqual({
- n_input, n_internal: n_internal-1, n_output,
- genes: [
- [0, 1, 0],
- [1, 2, 1],
- [1, 2, 2],
- [2, 3, 3],
- ],
- });
-
- expect(mut_genome_contract(genome, 0.99)).toEqual({
- n_input, n_internal: n_internal-1, n_output,
- genes: [
- [0, 1, 0],
- [1, 2, 1],
- [2, 3, 2],
- [2, 3, 3],
- ],
- });
-});
-
-
-
-test('insert new genes', () => {
- const n_input = 1;
- const n_internal = 2;
- const n_output = 1;
- const weight_max = 4;
-
- expect(mut_genome_insert({
- n_input, n_internal, n_output,
- genes: []
- }, weight_max, 0, 0.5, 0)).toEqual({
- n_input, n_internal, n_output,
- genes: [[0, 2, -4]]
- });
-
- expect(mut_genome_insert({
- n_input, n_internal, n_output,
- genes: [[0, 2, -4]]
- }, weight_max, 0.99, 0, 1)).toEqual({
- n_input, n_internal, n_output,
- genes: [[0, 2, -4], [2, 1, 4]]
- });
-});
-
-
-test('remove genes', () => {
- const n_input = 0;
- const n_output = 0;
- const n_internal = 3;
- const genome = {
- n_input, n_internal, n_output,
- genes: [[0, 1, 0], [1, 2, 0]],
- };
-
- expect(mut_genome_delete(genome, 0.0)).toEqual({
- n_input, n_internal, n_output,
- genes: [[1, 2, 0]],
- });
-
- expect(mut_genome_delete(genome, 0.99)).toEqual({
- n_input, n_internal, n_output,
- genes: [[0, 1, 0]],
- });
-});
diff --git a/src/genome/trial.js b/src/genome/trial.js deleted file mode 100644 index 2ce23bf..0000000 --- a/src/genome/trial.js +++ /dev/null @@ -1,37 +0,0 @@ -import { - get_size, - mut_genome_insert, mutate_genome, -} from './genome.js'; - -const recurse = (f, x0, n) => { - if (n == 0) { - return x0; - } else { - return f(recurse(f, x0, n-1)); - } -}; - - -const n_input = 5; -const n_output = 5; - - -const [_1, _2, _3, genome] = recurse( - s => mut_genome_insert( - s, 4, - Math.random(), Math.random(), Math.random() - ), - [n_input, 10, n_output, []], - 20); - - -const n_internal = get_size(n_input, n_output, genome) - n_input - n_output; -console.log([n_input, n_internal, n_output, genome]); - -const mutation = recurse( - s => mutate_genome(s, 4), - [n_input, n_internal, n_output, genome], - 40 -); - -console.log(mutation); |