'use strict'; import { network } from '../mind/topology'; export const mutation_type = Object.freeze({ none: 'none', source: 'source', sink: 'sink', weight: 'weight', }); // clamp a number in the range [0, infinity) function nonneg(x) { if (x < 0) { return 0; } else { return x; } } // mutate a gene export function mutate(gene, type, value) { const [ source, sink, weight ] = gene; switch(type) { case mutation_type.none: return [...gene]; case mutation_type.source: if (value <= 0.5) { return [ nonneg(source-1), sink, weight ]; } else { return [ source+1, sink, weight ]; } case mutation_type.sink: if (value <= 0.5) { return [ source, nonneg(sink-1), weight ]; } else { return [ source, sink+1, weight ]; } case mutation_type.weight: const w = (8*value) - 4; return [ source, sink, 0.5*(w + weight) ]; default: throw new Error(`unknown mutation type: '${type}'`); }; } // check if a given genome is valid and compute its size export function get_size(num_input, num_output, genome) { const [ max_index, max_weight ] = genome.reduce( ([max_index, max_weight ], [ source, sink, weight]) => [ Math.max(max_index, source, sink), Math.max(max_weight, Math.abs(weight)), ], [ 0, 0 ] ); if (max_index < num_input + num_output - 1) { return -1; } else if (max_weight > 4.0) { return -1; } else { return max_index + 1; } } export function parse_genome(num_input, num_output, genome) { const size = get_size(num_input, num_output, genome); if (size < 0) { // bad genome throw new Error('invalid genome sequence!'); } const n = genome.reduce( (acc, [source, sink, weight]) => acc.connect(source, sink, weight), network(num_input, size-num_input-num_output, num_output) ); return n; }