summaryrefslogtreecommitdiff
path: root/src/genome/genome.js
blob: d2650b5791bf7f97c1a5b7b2930da89044df17a1 (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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
'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_contract(
  [n_input, n_internal, n_output, genome], r
) {
  const contract_idx = Math.floor(n_internal * r) + n_input;
  const new_source = (source) => source >= contract_idx ? source-1 : source;
  const new_sink   = (sink)   => sink > contract_idx ? sink-1 : sink;

  const new_genome = genome.map(([source, sink, weight]) => [
    new_source(source), 
    new_sink(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]],
  ];
}


export function mut_genome_delete(
  [n_input, n_internal, n_output, genome], r
) {
  const del_idx = Math.floor(r * genome.length);
  const new_genome = genome.filter((_, idx) => idx != del_idx);
  return [n_input, n_internal, n_output, new_genome];
}