summaryrefslogtreecommitdiff
path: root/src/genome/genome.js
blob: 0f7275f36cd0f9b586be8bb6cbddb937f3690ff5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
'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;
}