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