/
LivingTreeGene.swift
130 lines (112 loc) · 4.16 KB
/
LivingTreeGene.swift
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
//
// LivingTreeGene.swift
// SwiftGenetics
//
// Created by Santiago Gonzalez on 11/15/18.
// Copyright © 2018 Santiago Gonzalez. All rights reserved.
//
import Foundation
/// An element in a genetic tree, which can recursively contain a subtree of tree genes.
final class LivingTreeGene<GeneType: TreeGeneType>: Gene {
typealias Environment = LivingTreeEnvironment
/// The sampling template that's used for the gene types.
var template: TreeGeneTemplate<GeneType>
/// The gene's type marker.
var geneType: GeneType
/// A weak reference to the gene's parent gene, or `nil` if it's the root.
weak var parent: LivingTreeGene?
/// Owned references to child genes.
var children: [LivingTreeGene]
/// A coefficient that isn't modified during evolution. It's a placeholder
/// that can be used later with CMA-ES.
var coefficient: Double?
/// Whether the gene takes a coefficient.
var allowsCoefficient: Bool
/// Creates a new tree gene.
init(_ template: TreeGeneTemplate<GeneType>, geneType: GeneType, parent: LivingTreeGene?, children: [LivingTreeGene], allowsCoefficient: Bool = true) {
self.template = template
self.geneType = geneType
self.parent = parent
self.children = children
self.allowsCoefficient = allowsCoefficient
}
func mutate(rate: Double, environment: Environment) {
guard Double.random(in: 0..<1) < rate else { return }
performGeneTypeSpecificMutations(rate: rate, environment: environment)
var madeStructuralMutation = false
// Deletion mutations.
if Double.random(in: 0..<1) < environment.structuralMutationDeletionRate {
if !children.isEmpty {
children = []
geneType = template.leafTypes.randomElement()!
madeStructuralMutation = true
}
}
// Addition mutations.
if Double.random(in: 0..<1) < environment.structuralMutationAdditionRate {
if children.isEmpty {
geneType = template.nonLeafTypes.randomElement()!
if geneType.isBinaryType {
children = [
LivingTreeGene(template, geneType: template.leafTypes.randomElement()!, parent: self, children: []),
LivingTreeGene(template, geneType: template.leafTypes.randomElement()!, parent: self, children: [])
]
} else if geneType.isUnaryType {
children = [LivingTreeGene(template, geneType: template.leafTypes.randomElement()!, parent: self, children: [])]
} else if geneType.isLeafType {
// nop
} else {
fatalError()
}
madeStructuralMutation = true
}
}
// Attempt to mutate type, maintaining the same structure, only if a
// structural mutation has not already been made.
if !madeStructuralMutation {
if geneType.isBinaryType {
geneType = template.binaryTypes.filter { $0 != geneType }.randomElement()!
} else if geneType.isUnaryType {
geneType = template.unaryTypes.filter { $0 != geneType }.randomElement() ?? template.unaryTypes.first!
} else if geneType.isLeafType {
geneType = template.leafTypes.filter { $0 != geneType }.randomElement()!
} else {
fatalError()
}
}
}
/// Performs a bottom-up, depth-first enumeration of the tree, including self.
func bottomUpEnumerate(eachNode fn: (LivingTreeGene) -> ()) {
for child in children {
child.bottomUpEnumerate(eachNode: fn)
}
fn(self)
}
/// Returns all nodes in the subtree, including the current gene.
var allNodes: [LivingTreeGene] {
var nodes: [LivingTreeGene] = [self]
for child in children {
nodes.append(contentsOf: child.allNodes)
}
return nodes
}
/// Creates a deep copy of the gene.
func copy(withParent: LivingTreeGene? = nil) -> LivingTreeGene {
let newGene = LivingTreeGene(template, geneType: geneType, parent: withParent, children: [], allowsCoefficient: allowsCoefficient)
newGene.children = children.map { $0.copy(withParent: newGene) }
return newGene
}
/// Rebuilds parent connections from child connections.
func recursivelyResetParents() {
for child in children {
child.parent = self
child.recursivelyResetParents()
}
}
}
extension LivingTreeGene {
/// Perform mutations that are specific to the living tree's `GeneType`.
func performGeneTypeSpecificMutations(rate: Double, environment: Environment) {
// Default implementation is intentionally empty.
}
}