diff options
author | sanine <sanine.not@pm.me> | 2023-10-30 02:14:17 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2023-10-30 02:14:17 -0500 |
commit | 70252c2ef37ddf974349fa092dce92782ffd302a (patch) | |
tree | 746b648674bb2b7befbd17c38d9e2d01ff4adfb3 /src | |
parent | a32853e60029fa7f08d4d713ee613ee03196fbef (diff) |
add genome creation & mutation trial
Diffstat (limited to 'src')
-rw-r--r-- | src/genome/genome.js | 87 | ||||
-rw-r--r-- | src/genome/trial.js | 37 | ||||
-rw-r--r-- | src/mind/topology.js | 2 | ||||
-rw-r--r-- | src/util.js | 6 |
4 files changed, 130 insertions, 2 deletions
diff --git a/src/genome/genome.js b/src/genome/genome.js index d2650b5..bc569ff 100644 --- a/src/genome/genome.js +++ b/src/genome/genome.js @@ -1,6 +1,7 @@ 'use strict';
-import { network } from '../mind/topology';
+import { random_choice } from '../util.js';
+import { network } from '../mind/topology.js';
// check if a given genome is valid and compute its size
@@ -25,6 +26,7 @@ export function get_size(num_input, num_output, genome) { }
+// parse a genome into a useable neural net
export function parse_genome(num_input, num_output, genome) {
const size = get_size(num_input, num_output, genome);
if (size < 0) {
@@ -49,6 +51,7 @@ function clamp(value, min, max) { 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;
@@ -62,6 +65,7 @@ export function mut_gene_source(n_input, n_internal, n_output, gene, r) { }
+// 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;
@@ -75,6 +79,9 @@ export function mut_gene_sink(n_input, n_internal, n_output, gene, r) { }
+// 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;
@@ -90,6 +97,9 @@ export function mut_gene_weight(weight_max, gene, r) { }
+
+// 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(
[n_input, n_internal, n_output, genome], r
) {
@@ -106,11 +116,16 @@ export function mut_genome_expand( }
+// 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(
[n_input, n_internal, n_output, genome], r
) {
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_genome = genome.map(([source, sink, weight]) => [
@@ -125,6 +140,7 @@ export function mut_genome_contract( }
+// append a newly generated gene to the end of the genome
export function mut_genome_insert(
[n_input, n_internal, n_output, genome],
weight_max,
@@ -141,6 +157,7 @@ export function mut_genome_insert( }
+// delete a gene from the genome
export function mut_genome_delete(
[n_input, n_internal, n_output, genome], r
) {
@@ -148,3 +165,71 @@ export function mut_genome_delete( const new_genome = genome.filter((_, idx) => idx != del_idx);
return [n_input, n_internal, n_output, new_genome];
}
+
+
+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/trial.js b/src/genome/trial.js new file mode 100644 index 0000000..2ce23bf --- /dev/null +++ b/src/genome/trial.js @@ -0,0 +1,37 @@ +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); diff --git a/src/mind/topology.js b/src/mind/topology.js index 576ee83..127351e 100644 --- a/src/mind/topology.js +++ b/src/mind/topology.js @@ -1,6 +1,6 @@ 'use strict'; -import { create } from '../util'; +import { create } from '../util.js'; const DEFAULT_WEIGHT_MAX = 4; diff --git a/src/util.js b/src/util.js index 9f52551..4e23d9d 100644 --- a/src/util.js +++ b/src/util.js @@ -8,3 +8,9 @@ export function create(obj, proto=Object.prototype) { return Object.create(proto, props);
};
+
+
+export function random_choice(collection, r) {
+ const idx = Math.floor(collection.length * r);
+ return collection[idx];
+}
|