summaryrefslogtreecommitdiff
path: root/src/genome/genome.js
blob: d35b68cbfaf74c28e50e53b114a4d385e839492e (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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
'use strict';

import { network } from '../mind/topology';


// 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;
}


// --===== mutations =====--

function clamp(value, min, max) {
  if (value > max) { return max; }
  if (value < min) { return min; }
  return value;
}

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,
  ];
}


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,
  ];
}


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),
  ];
}


export function mut_genome_expand(
  [n_input, n_internal, n_output, genome], r
) {
  const expand_index = Math.floor(n_internal * r) + n_input;
  const new_genome = genome.map(([source, sink, weight]) => [
    source >= expand_index ? source+1 : source,
    sink >= expand_index ? sink+1 : sink,
    weight,
  ]);

  return [
    n_input, n_internal+1, n_output, new_genome,
  ];
}


export function mut_genome_insert(
  [n_input, n_internal, n_output, genome], 
  weight_max, 
  r1, r2, r3
) {
  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 [
    n_input, n_internal, n_output,
    [...genome, [source, sink, weight]],
  ];
}