pikaia 0.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pikaia-0.0.2/.gitignore +42 -0
- pikaia-0.0.2/LICENSE +19 -0
- pikaia-0.0.2/PKG-INFO +90 -0
- pikaia-0.0.2/README.md +72 -0
- pikaia-0.0.2/pyproject.toml +36 -0
- pikaia-0.0.2/src/pikaia/__init__.py +0 -0
- pikaia-0.0.2/src/pikaia/alg.py +437 -0
- pikaia-0.0.2/src/pikaia/examples.py +107 -0
- pikaia-0.0.2/src/pikaia/plot.py +162 -0
- pikaia-0.0.2/src/pikaia/search.py +116 -0
pikaia-0.0.2/.gitignore
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# pycache
|
|
2
|
+
__pycache__
|
|
3
|
+
|
|
4
|
+
# ignore ALL .log files
|
|
5
|
+
## Core latex/pdflatex auxiliary files:
|
|
6
|
+
*.aux
|
|
7
|
+
*.lof
|
|
8
|
+
*.log
|
|
9
|
+
*.lot
|
|
10
|
+
*.fls
|
|
11
|
+
*.out
|
|
12
|
+
*.toc
|
|
13
|
+
*.fmt
|
|
14
|
+
*.fot
|
|
15
|
+
*.cb
|
|
16
|
+
*.cb2
|
|
17
|
+
.*.lb
|
|
18
|
+
|
|
19
|
+
## Bibliography auxiliary files (bibtex/biblatex/biber):
|
|
20
|
+
*.bbl
|
|
21
|
+
*.bbl-SAVE-ERROR
|
|
22
|
+
*.bcf
|
|
23
|
+
*.blg
|
|
24
|
+
*-blx.aux
|
|
25
|
+
*-blx.bib
|
|
26
|
+
*.run.xml
|
|
27
|
+
|
|
28
|
+
*.zip
|
|
29
|
+
|
|
30
|
+
src/public/*.pdf
|
|
31
|
+
src/public/*.png
|
|
32
|
+
src/public/test
|
|
33
|
+
src/public/venv
|
|
34
|
+
src/public/dist
|
|
35
|
+
src/public/__pycache__/
|
|
36
|
+
src/public/geneticai/__pycache__/
|
|
37
|
+
tmp/
|
|
38
|
+
tutorial_env/
|
|
39
|
+
venv/
|
|
40
|
+
tex/*.pdf
|
|
41
|
+
tex/*.png
|
|
42
|
+
dist/
|
pikaia-0.0.2/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2018 The Python Packaging Authority
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
pikaia-0.0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: pikaia
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Data analysis with evolutionary simulation
|
|
5
|
+
Project-URL: Homepage, https://github.com/danube-ai/pikaia
|
|
6
|
+
Project-URL: Issues, https://github.com/danube-ai/pikaia/issues
|
|
7
|
+
Author-email: Philipp Wissgott <philipp@danube.ai>, Andreas Roschal <andreas@danube.ai>, Martin Bär <martin@danube.ai>
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: AI,data analysis,evolutionary simulation
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
|
+
Requires-Dist: matplotlib>=3.6.3
|
|
14
|
+
Requires-Dist: numpy
|
|
15
|
+
Requires-Dist: pandas>=1.5.3
|
|
16
|
+
Requires-Dist: string
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# pikaia - Genetic AI
|
|
20
|
+
|
|
21
|
+
pikaia is the Python implementation of Genetic AI (evolutionary simulation for data analysis).
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
Use the package manager [pip](https://pip.pypa.io/en/stable/) to install pikaia
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install pikaia
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
We provide here the code for the "hello_model" example
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
import pikaia
|
|
37
|
+
|
|
38
|
+
import pikaia
|
|
39
|
+
import pikaia.alg
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
rawdata = np.zeros([3,3])
|
|
43
|
+
rawdata[:,:] = [[ 300, 10, 2],
|
|
44
|
+
[ 600, 5, 2],
|
|
45
|
+
[1500, 4, 1]]
|
|
46
|
+
# defines the variant fitness rules
|
|
47
|
+
gvfitnessrules = ["inv_percentage", "inv_percentage", "inv_percentage"]
|
|
48
|
+
# converts the raw data to a genetic population
|
|
49
|
+
data = pikaia.alg.Population(rawdata, gvfitnessrules)
|
|
50
|
+
# defines the used evolutionary strategies
|
|
51
|
+
strategy = ["GS Dominant", "OS Balanced"]
|
|
52
|
+
iterations = 1
|
|
53
|
+
|
|
54
|
+
# creating the genetic model
|
|
55
|
+
model = pikaia.alg.Model(data, strategy)
|
|
56
|
+
|
|
57
|
+
initialgenefitness = [1.0/3.0, 1.0/3.0, 1.0/3.0]
|
|
58
|
+
# returns the gene fitness values after 1 iteration
|
|
59
|
+
model.complete_run(initialgenefitness, iterations)
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Examples
|
|
64
|
+
```python
|
|
65
|
+
# provides the data for a small decision problem
|
|
66
|
+
example3x3 = pikaia.examples.assemble_example("3x3-DomBal+AltSal")
|
|
67
|
+
|
|
68
|
+
# provides the data for a real-world decision problem
|
|
69
|
+
example10x5 = pikaia.examples.assemble_example("10x5-DomBal+AltSal")
|
|
70
|
+
|
|
71
|
+
# use genetic ai to search a datafile using keywords and rank results
|
|
72
|
+
# for a more detailed example we refer to examples/geneticAI_run_search_example.py
|
|
73
|
+
search = pikaia.search.Search(data, orgs_labels, gens_labels)
|
|
74
|
+
fitnessOrganisms, fitnessGenes = search.search_request(query, top_k=5)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
For details see examples/README.md.
|
|
78
|
+
|
|
79
|
+
## Scientific Background
|
|
80
|
+
|
|
81
|
+
Please find the preprint of Genetic AI [here](http://arxiv.org/abs/2501.19113)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
In Genetic AI, we convert a data problem to a model of genes and organisms. Afterwards, we run evolutionary simulations to obtain understanding of the input data.
|
|
85
|
+
|
|
86
|
+
Genetic AI is an AI that does not use training data to 'learn' but fully autonomously analyzes a problem. This is done by evolutionary strategies that cover certain 'behavior' and correlations of the input data.
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
[MIT](https://choosealicense.com/licenses/mit/)
|
pikaia-0.0.2/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# pikaia - Genetic AI
|
|
2
|
+
|
|
3
|
+
pikaia is the Python implementation of Genetic AI (evolutionary simulation for data analysis).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Use the package manager [pip](https://pip.pypa.io/en/stable/) to install pikaia
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install pikaia
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
We provide here the code for the "hello_model" example
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
import pikaia
|
|
19
|
+
|
|
20
|
+
import pikaia
|
|
21
|
+
import pikaia.alg
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
rawdata = np.zeros([3,3])
|
|
25
|
+
rawdata[:,:] = [[ 300, 10, 2],
|
|
26
|
+
[ 600, 5, 2],
|
|
27
|
+
[1500, 4, 1]]
|
|
28
|
+
# defines the variant fitness rules
|
|
29
|
+
gvfitnessrules = ["inv_percentage", "inv_percentage", "inv_percentage"]
|
|
30
|
+
# converts the raw data to a genetic population
|
|
31
|
+
data = pikaia.alg.Population(rawdata, gvfitnessrules)
|
|
32
|
+
# defines the used evolutionary strategies
|
|
33
|
+
strategy = ["GS Dominant", "OS Balanced"]
|
|
34
|
+
iterations = 1
|
|
35
|
+
|
|
36
|
+
# creating the genetic model
|
|
37
|
+
model = pikaia.alg.Model(data, strategy)
|
|
38
|
+
|
|
39
|
+
initialgenefitness = [1.0/3.0, 1.0/3.0, 1.0/3.0]
|
|
40
|
+
# returns the gene fitness values after 1 iteration
|
|
41
|
+
model.complete_run(initialgenefitness, iterations)
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Examples
|
|
46
|
+
```python
|
|
47
|
+
# provides the data for a small decision problem
|
|
48
|
+
example3x3 = pikaia.examples.assemble_example("3x3-DomBal+AltSal")
|
|
49
|
+
|
|
50
|
+
# provides the data for a real-world decision problem
|
|
51
|
+
example10x5 = pikaia.examples.assemble_example("10x5-DomBal+AltSal")
|
|
52
|
+
|
|
53
|
+
# use genetic ai to search a datafile using keywords and rank results
|
|
54
|
+
# for a more detailed example we refer to examples/geneticAI_run_search_example.py
|
|
55
|
+
search = pikaia.search.Search(data, orgs_labels, gens_labels)
|
|
56
|
+
fitnessOrganisms, fitnessGenes = search.search_request(query, top_k=5)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
For details see examples/README.md.
|
|
60
|
+
|
|
61
|
+
## Scientific Background
|
|
62
|
+
|
|
63
|
+
Please find the preprint of Genetic AI [here](http://arxiv.org/abs/2501.19113)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
In Genetic AI, we convert a data problem to a model of genes and organisms. Afterwards, we run evolutionary simulations to obtain understanding of the input data.
|
|
67
|
+
|
|
68
|
+
Genetic AI is an AI that does not use training data to 'learn' but fully autonomously analyzes a problem. This is done by evolutionary strategies that cover certain 'behavior' and correlations of the input data.
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
[MIT](https://choosealicense.com/licenses/mit/)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling==1.26.3"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pikaia"
|
|
7
|
+
version = "0.0.2"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Philipp Wissgott", email="philipp@danube.ai" },
|
|
10
|
+
{ name="Andreas Roschal", email="andreas@danube.ai" },
|
|
11
|
+
{ name="Martin Bär", email="martin@danube.ai" }
|
|
12
|
+
]
|
|
13
|
+
description = "Data analysis with evolutionary simulation"
|
|
14
|
+
keywords = ["data analysis", "AI", "evolutionary simulation"]
|
|
15
|
+
readme = "README.md"
|
|
16
|
+
requires-python = ">=3.8"
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
]
|
|
21
|
+
license = "MIT"
|
|
22
|
+
license-files = ["LICEN[CS]E*"]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"numpy",
|
|
25
|
+
"matplotlib>=3.6.3",
|
|
26
|
+
"string",
|
|
27
|
+
"pandas>=1.5.3"
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[tool.hatch.build.targets.sdist]
|
|
31
|
+
include = ["*.py"]
|
|
32
|
+
exclude = ["tmp", "examples"]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/danube-ai/pikaia"
|
|
36
|
+
Issues = "https://github.com/danube-ai/pikaia/issues"
|
|
File without changes
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
class Model:
|
|
4
|
+
def __init__(self, inputdata, strategies,
|
|
5
|
+
genelabels=None, orgslabels=None,
|
|
6
|
+
linestyles=None, markerstyles=None):
|
|
7
|
+
self._data = inputdata
|
|
8
|
+
self._strategies = strategies
|
|
9
|
+
self._genelabels = genelabels
|
|
10
|
+
self._orgslabels = orgslabels
|
|
11
|
+
self._linestyles = linestyles
|
|
12
|
+
self._markerstyles = markerstyles
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def data(self):
|
|
16
|
+
return self._data
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def complete_run(self, initialgenefitness, maxiteration=1, epsilon=None, silent=False):
|
|
20
|
+
"""Runs an evolutionary simulation.
|
|
21
|
+
|
|
22
|
+
Runs a evolutionary simulation until either maxiter
|
|
23
|
+
is reached or the difference of gene fitness values
|
|
24
|
+
from two consecutive iterations are below epsilon.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
initialgenefitness (vector of shape `(1, m)`):
|
|
28
|
+
The initial fitness used in this simulation.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
the similarity matrix `(n, n)`,
|
|
32
|
+
"""
|
|
33
|
+
self._maxiter = maxiteration
|
|
34
|
+
self._epsilon = epsilon
|
|
35
|
+
genefitness = initialgenefitness
|
|
36
|
+
print("initial gene fitness = ", genefitness)
|
|
37
|
+
initialorganismfitness = compute_all_organism_fitness(self.data.population, genefitness)
|
|
38
|
+
if not silent:
|
|
39
|
+
print("initial organism fitness = ", initialorganismfitness)
|
|
40
|
+
|
|
41
|
+
#iter = self._maxiter
|
|
42
|
+
n = self.data.population.shape[0]
|
|
43
|
+
m = self.data.population.shape[1]
|
|
44
|
+
# helper structures storing iterations
|
|
45
|
+
self._gps = np.zeros([self._maxiter+1, m])
|
|
46
|
+
self._ofs = np.zeros([self._maxiter+1, n])
|
|
47
|
+
self._gps[0,:] = genefitness
|
|
48
|
+
self._ofs[0,:] = initialorganismfitness
|
|
49
|
+
|
|
50
|
+
# compute similarities necessary for some strategies
|
|
51
|
+
genesimilarity = compute_gene_similarity(self.data.population)
|
|
52
|
+
orgsimilarity = compute_organism_similarity(self.data.population)
|
|
53
|
+
|
|
54
|
+
for k in range(0, self._maxiter):
|
|
55
|
+
if not silent:
|
|
56
|
+
print("+++ Iteration ", k," +++")
|
|
57
|
+
genefitness, fitness = iteration(self.data.population, genefitness,
|
|
58
|
+
genesimilarity, orgsimilarity,
|
|
59
|
+
self._strategies, silent=silent)
|
|
60
|
+
self._gps[k+1,:] = genefitness
|
|
61
|
+
self._ofs[k+1,:] = fitness
|
|
62
|
+
if not silent:
|
|
63
|
+
print("gene fitness = ", genefitness)
|
|
64
|
+
print("organism fitness = ", fitness)
|
|
65
|
+
delta = np.linalg.norm(self._gps[k+1,:]-self._gps[k,:])
|
|
66
|
+
self._iterESE = k + 1
|
|
67
|
+
if self._epsilon is not None and delta < self._epsilon:
|
|
68
|
+
print("reached ESE after ", self._iterESE, "iterations. Final delta = ", delta)
|
|
69
|
+
break
|
|
70
|
+
return genefitness, fitness
|
|
71
|
+
|
|
72
|
+
class Population:
|
|
73
|
+
def __init__(self, rawdata, gvfitnessrule):
|
|
74
|
+
self._rawdata = rawdata
|
|
75
|
+
self._n = self._rawdata.shape[0] #number of organisms
|
|
76
|
+
self._m = self._rawdata.shape[1] #number of genes
|
|
77
|
+
self._gvfitnessrules = gvfitnessrule
|
|
78
|
+
self._population = np.zeros([self._n,self._m])
|
|
79
|
+
for j in range(0, self._m):
|
|
80
|
+
# the populations derive from the raw data by applying gene variant
|
|
81
|
+
# fitness functions
|
|
82
|
+
self._population[:,j] = compute_gene_variant_fitness(self._rawdata[:,j],
|
|
83
|
+
self._gvfitnessrules[j])
|
|
84
|
+
print("Population = ", self._population)
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def n(self):
|
|
88
|
+
return self._n
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def m(self):
|
|
92
|
+
return self._m
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def rawdata(self):
|
|
96
|
+
return self._rawdata
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def population(self):
|
|
100
|
+
return self._population
|
|
101
|
+
|
|
102
|
+
def get_uniform_gene_fitness(self):
|
|
103
|
+
genefitness = np.zeros([self._m])
|
|
104
|
+
for j in range(0, self._m):
|
|
105
|
+
genefitness[j] = 1.0/self._m
|
|
106
|
+
return genefitness
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def compute_gene_variant_fitness(genedata, gvfrule):
|
|
110
|
+
"""Computes the gene variant fitness from raw data.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
genedata (vector of shape `(n, 1)`):
|
|
114
|
+
The input data.
|
|
115
|
+
gvrule:
|
|
116
|
+
The gene variant fitness function applied.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
A float, the gene variant fitness.
|
|
120
|
+
"""
|
|
121
|
+
if "inv" in gvfrule:
|
|
122
|
+
invert = True
|
|
123
|
+
else:
|
|
124
|
+
invert = False
|
|
125
|
+
|
|
126
|
+
if "cap" in gvfrule:
|
|
127
|
+
cap = True
|
|
128
|
+
else:
|
|
129
|
+
cap = False
|
|
130
|
+
n = len(genedata)
|
|
131
|
+
maxvariant = max(genedata)
|
|
132
|
+
minvariant = min(genedata)
|
|
133
|
+
gvfitness = np.zeros(n)
|
|
134
|
+
for i in range(0, n):
|
|
135
|
+
if invert:
|
|
136
|
+
if cap:
|
|
137
|
+
gvfitness[i] = (maxvariant - genedata[i])/(maxvariant-minvariant) if (maxvariant-minvariant) != 0 else 0
|
|
138
|
+
else:
|
|
139
|
+
gvfitness[i] = (maxvariant - genedata[i])/maxvariant if maxvariant > 0 else 0
|
|
140
|
+
else:
|
|
141
|
+
if cap:
|
|
142
|
+
gvfitness[i] = genedata[i]/(maxvariant-minvariant) if maxvariant > 0 else 0
|
|
143
|
+
else:
|
|
144
|
+
gvfitness[i] = genedata[i]/maxvariant if maxvariant > 0 else 0
|
|
145
|
+
return gvfitness
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def compute_organism_fitness(organism, genefitness):
|
|
149
|
+
"""Computes the (linear) organism fitness.
|
|
150
|
+
|
|
151
|
+
Dot product of organism and genefitness.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
organism (vector of shape `(1, m)`):
|
|
155
|
+
The gene variant fitness values of a single organism.
|
|
156
|
+
genefitness (vector of shape `(1, m)`):
|
|
157
|
+
The list of gene fitness values.
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
A float, the linear organism fitness.
|
|
161
|
+
"""
|
|
162
|
+
ofitness = 0.0
|
|
163
|
+
for j in range(0, len(organism)):
|
|
164
|
+
ofitness += organism[j]*genefitness[j]
|
|
165
|
+
return ofitness
|
|
166
|
+
|
|
167
|
+
def compute_all_organism_fitness(population, genefitness):
|
|
168
|
+
"""Computes the (linear) organism fitness values for a population.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
population (matrix of shape `(n, m)`):
|
|
172
|
+
The population data in matrix form, rows are organisms,
|
|
173
|
+
colums genes.
|
|
174
|
+
genefitness (vector of shape `(1, m)`):
|
|
175
|
+
The list of gene fitness values.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
A vector of shape `(n, 1)`, the list of fitness values
|
|
179
|
+
of the organisms in the population.
|
|
180
|
+
"""
|
|
181
|
+
n = population.shape[0]
|
|
182
|
+
populationfitness = np.zeros(n)
|
|
183
|
+
for i in range(0, n):
|
|
184
|
+
populationfitness[i] = compute_organism_fitness(population[i,:], genefitness)
|
|
185
|
+
return populationfitness
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def compute_gene_deltas(geneindex, genevariantfitness, gene, organism, genefitness,
|
|
189
|
+
genesimilarity, strategy="GS Dominant"):
|
|
190
|
+
"""Computes delta values stemming from the gene strategy.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
geneindex (integer):
|
|
194
|
+
The index j for which gene index the delta is to be computed.
|
|
195
|
+
genevariantfitness (float):
|
|
196
|
+
the gene variant fitness at i,j.
|
|
197
|
+
gene (float vector of shape `(1, n)`):
|
|
198
|
+
the current gene vector.
|
|
199
|
+
organism (float vector of shape `(m, 1)`):
|
|
200
|
+
the current organism vector.
|
|
201
|
+
genefitness (float vector of shape `(1, m)`):
|
|
202
|
+
The list of gene fitness values.
|
|
203
|
+
genesimilarity (float vector of shape `(1, m)`):
|
|
204
|
+
The kinship of the current gene to the others.
|
|
205
|
+
strategy (string):
|
|
206
|
+
The evolutionary strategy to be applied.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
A float, the Delta(i,j) value for a particular gene
|
|
210
|
+
and organism, respectively.
|
|
211
|
+
"""
|
|
212
|
+
n = len(gene)
|
|
213
|
+
m = len(organism)
|
|
214
|
+
if strategy == "GS Dominant":
|
|
215
|
+
deltaG = 1/float(n)*genefitness[geneindex]*(genevariantfitness-1/2)*2
|
|
216
|
+
elif strategy == "GS Selfish":
|
|
217
|
+
deltaG = 0
|
|
218
|
+
for j in range(0, m):
|
|
219
|
+
if j == geneindex:
|
|
220
|
+
continue
|
|
221
|
+
deltaG += -1/float(n)*genesimilarity[j]*genefitness[geneindex]*\
|
|
222
|
+
(genevariantfitness-1/2)*(organism[j] - genevariantfitness)
|
|
223
|
+
deltaG = deltaG/float(m)
|
|
224
|
+
elif strategy == "GS Kin-Altruistic":
|
|
225
|
+
deltaG = 0
|
|
226
|
+
for j in range(0, m):
|
|
227
|
+
if j == geneindex:
|
|
228
|
+
continue
|
|
229
|
+
deltaG += 1/float(n)*(0.5-genesimilarity[j])*genefitness[geneindex]*\
|
|
230
|
+
(genevariantfitness-1/2)*(organism[j] - genevariantfitness)
|
|
231
|
+
deltaG = deltaG/float(m)
|
|
232
|
+
elif strategy == "GS Altruistic":
|
|
233
|
+
deltaG = 0
|
|
234
|
+
for j in range(0, m):
|
|
235
|
+
if j == geneindex:
|
|
236
|
+
continue
|
|
237
|
+
deltaG += 1/float(n)*genesimilarity[j]*genefitness[geneindex]*(genevariantfitness-1/2)*\
|
|
238
|
+
genefitness[j]*(organism[j] - genevariantfitness)
|
|
239
|
+
deltaG = deltaG/float(m)
|
|
240
|
+
elif strategy == "GS None":
|
|
241
|
+
deltaG = 0
|
|
242
|
+
else:
|
|
243
|
+
raise NameError("Unknown gene strategy:" + strategy)
|
|
244
|
+
|
|
245
|
+
return deltaG
|
|
246
|
+
|
|
247
|
+
def compute_organism_deltas(orgindex, population, genefitness,
|
|
248
|
+
orgfitness, orgsimilarity, strategy="OS Balance"):
|
|
249
|
+
"""Computes delta values stemming from the organism strategy.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
orgindex (integer):
|
|
253
|
+
The index i of the current organism.
|
|
254
|
+
population (matrix of shape `(n, m)`):
|
|
255
|
+
The full population matrix obtained by applying the
|
|
256
|
+
gene variant fitness functions to the input data.
|
|
257
|
+
genefitness (float vector of shape `(1, m)`):
|
|
258
|
+
The list of gene fitness values.
|
|
259
|
+
orgfitness (float vector of shape `(n, 1)`):
|
|
260
|
+
The list of organism fitness values.
|
|
261
|
+
orgsimilarity (float vector of shape `(n, 1)`):
|
|
262
|
+
The kinship of the current organism to the others.
|
|
263
|
+
strategy (string):
|
|
264
|
+
The evolutionary strategy to be applied.
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
A vector shape `(0, m)` containing the changes to the gene
|
|
268
|
+
fitness for the particular organism.
|
|
269
|
+
"""
|
|
270
|
+
n = population.shape[0]
|
|
271
|
+
m = population.shape[1]
|
|
272
|
+
|
|
273
|
+
deltaO = np.zeros(m)
|
|
274
|
+
if strategy == "OS Balanced":
|
|
275
|
+
for j in range(0, m):
|
|
276
|
+
genecontribution = population[orgindex, j]*genefitness[j]
|
|
277
|
+
if orgfitness[orgindex] == 0:
|
|
278
|
+
deltaO[j] = 0
|
|
279
|
+
else:
|
|
280
|
+
deltaO[j] = -1/n*(genecontribution/orgfitness[orgindex] - 1/m)*orgfitness[orgindex]
|
|
281
|
+
elif strategy == "OS Altruistic":
|
|
282
|
+
for i in range(0, n):
|
|
283
|
+
if i == orgindex:
|
|
284
|
+
continue
|
|
285
|
+
|
|
286
|
+
for j in range(0, m):
|
|
287
|
+
genecontribution = population[orgindex, j]*genefitness[j]
|
|
288
|
+
if orgfitness[orgindex] == 0:
|
|
289
|
+
deltaO[j] = 0
|
|
290
|
+
else:
|
|
291
|
+
deltaO[j] += -1/n*orgsimilarity[i]*(genecontribution/orgfitness[orgindex] - 1/m)*\
|
|
292
|
+
(1/n)*(orgfitness[orgindex]-orgfitness[i])
|
|
293
|
+
elif strategy == "OS Kin-Selfish":
|
|
294
|
+
for i in range(0, n):
|
|
295
|
+
if i == orgindex:
|
|
296
|
+
continue
|
|
297
|
+
|
|
298
|
+
for j in range(0, m):
|
|
299
|
+
genecontribution = population[orgindex, j]*genefitness[j]
|
|
300
|
+
if orgfitness[orgindex] == 0:
|
|
301
|
+
deltaO[j] = 0
|
|
302
|
+
else:
|
|
303
|
+
deltaO[j] += 1/n*(0.5-orgsimilarity[i])*(genecontribution/orgfitness[orgindex] - 1/m)*\
|
|
304
|
+
(1/n)*(orgfitness[orgindex]-orgfitness[i])
|
|
305
|
+
|
|
306
|
+
elif strategy == "OS Selfish":
|
|
307
|
+
for i in range(0, n):
|
|
308
|
+
if i == orgindex:
|
|
309
|
+
continue
|
|
310
|
+
|
|
311
|
+
for j in range(0, m):
|
|
312
|
+
genecontribution = population[orgindex, j]*genefitness[j]
|
|
313
|
+
if orgfitness[orgindex] == 0:
|
|
314
|
+
deltaO[j] = 0
|
|
315
|
+
else:
|
|
316
|
+
deltaO[j] += -1/n*orgsimilarity[i]*(genecontribution/orgfitness[orgindex] - 1/m)*\
|
|
317
|
+
(1/n)*(orgfitness[orgindex]-orgfitness[i])
|
|
318
|
+
elif strategy == "OS None":
|
|
319
|
+
pass
|
|
320
|
+
else:
|
|
321
|
+
raise NameError("Unknown organism strategy:" + strategy)
|
|
322
|
+
return deltaO
|
|
323
|
+
|
|
324
|
+
def iteration(population, genefitness, genesimilarity,
|
|
325
|
+
orgsimilarity, strategy, silent=False):
|
|
326
|
+
"""Runs a single, evolutionary step.
|
|
327
|
+
|
|
328
|
+
One iteration takes a given population and gene fitness,
|
|
329
|
+
calculates a new gene and organism fitness.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
population (matrix of shape `(n, m)`):
|
|
333
|
+
The population data in matrix form, rows are organisms,
|
|
334
|
+
colums genes.
|
|
335
|
+
genefitness (vector of shape `(1, m)`):
|
|
336
|
+
The list of gene fitness values.
|
|
337
|
+
genesimilarity (matrix of shape `(m, m)`):
|
|
338
|
+
The symmetric gene kinship matrix.
|
|
339
|
+
orgsimilarity (matrix of shape `(n, n)`):
|
|
340
|
+
The symmetric organism kinship matrix.
|
|
341
|
+
strategy (string vector of length 2):
|
|
342
|
+
Defines the used gene and organism strategy, respectively.
|
|
343
|
+
|
|
344
|
+
Returns:
|
|
345
|
+
Two vectors, first, the new gene fitness of shape `(1, m)`,
|
|
346
|
+
second, the organism fitness values of shape `(n, 1)`
|
|
347
|
+
"""
|
|
348
|
+
n = population.shape[0]
|
|
349
|
+
m = population.shape[1]
|
|
350
|
+
|
|
351
|
+
# get Delta contributions to gene fitness updates
|
|
352
|
+
deltaGs = np.zeros([n,m])
|
|
353
|
+
deltaOs = np.zeros([n,m])
|
|
354
|
+
orgfitness = np.zeros([n,1])
|
|
355
|
+
for i in range(0, n):
|
|
356
|
+
orgfitness[i] = compute_organism_fitness(population[i,:], genefitness)
|
|
357
|
+
|
|
358
|
+
for i in range(0, n):
|
|
359
|
+
deltaOs[i,:] = compute_organism_deltas(i, population, genefitness, orgfitness, orgsimilarity[i,:], strategy[1])
|
|
360
|
+
for j in range(0, m):
|
|
361
|
+
deltaGs[i,j] = compute_gene_deltas(j, population[i,j], population[:,j], population[i,:], genefitness,
|
|
362
|
+
genesimilarity[j,:], strategy[0])
|
|
363
|
+
if not silent:
|
|
364
|
+
print("deltaGs = ", deltaGs)
|
|
365
|
+
print("deltaOs = ", deltaOs)
|
|
366
|
+
deltas = deltaGs + deltaOs
|
|
367
|
+
deltaG = np.sum(deltaGs,axis=0)
|
|
368
|
+
deltaO = np.sum(deltaOs,axis=0)
|
|
369
|
+
delta = np.sum(deltas,axis=0)
|
|
370
|
+
if not silent:
|
|
371
|
+
print("deltaG = ", deltaG)
|
|
372
|
+
print("deltaO = ", deltaO)
|
|
373
|
+
print("delta = ", delta)
|
|
374
|
+
|
|
375
|
+
# apply replicator equations
|
|
376
|
+
newgenefitness = np.zeros(m)
|
|
377
|
+
for j in range(0, m):
|
|
378
|
+
newgenefitness[j] = genefitness[j]*(1+delta[j])
|
|
379
|
+
sumfitness = sum(newgenefitness)
|
|
380
|
+
for j in range(0, m):
|
|
381
|
+
newgenefitness[j] /= sumfitness
|
|
382
|
+
neworganismfitness = compute_all_organism_fitness(population, newgenefitness)
|
|
383
|
+
|
|
384
|
+
return newgenefitness, neworganismfitness
|
|
385
|
+
|
|
386
|
+
def compute_gene_similarity(population):
|
|
387
|
+
"""Computes the similarity/kinship matrix for genes.
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
population (matrix of shape `(n, m)`):
|
|
391
|
+
The population data in matrix form, rows are organisms,
|
|
392
|
+
colums genes.
|
|
393
|
+
|
|
394
|
+
Returns:
|
|
395
|
+
the similarity matrix `(m, m)`,
|
|
396
|
+
"""
|
|
397
|
+
n = population.shape[0]
|
|
398
|
+
m = population.shape[1]
|
|
399
|
+
genediversity = np.zeros([m,m])
|
|
400
|
+
for j in range(0,m):
|
|
401
|
+
for l in range(0,m):
|
|
402
|
+
tmp = np.linalg.norm(population[:,j] - population[:,l])
|
|
403
|
+
genediversity[j,l] += tmp
|
|
404
|
+
|
|
405
|
+
genesimilarity = 1 - genediversity/float(n)
|
|
406
|
+
print("Gene similarity = ")
|
|
407
|
+
print(genesimilarity)
|
|
408
|
+
return genesimilarity
|
|
409
|
+
|
|
410
|
+
def compute_organism_similarity(population):
|
|
411
|
+
"""Computes the similarity/kinship matrix for organisms.
|
|
412
|
+
|
|
413
|
+
Args:
|
|
414
|
+
population (matrix of shape `(n, m)`):
|
|
415
|
+
The population data in matrix form, rows are organisms,
|
|
416
|
+
colums genes.
|
|
417
|
+
|
|
418
|
+
Returns:
|
|
419
|
+
the similarity matrix `(n, n)`,
|
|
420
|
+
"""
|
|
421
|
+
n = population.shape[0]
|
|
422
|
+
m = population.shape[1]
|
|
423
|
+
orgdiversity = np.zeros([n,n])
|
|
424
|
+
for i in range(0, n):
|
|
425
|
+
for l in range(0, n):
|
|
426
|
+
tmp = np.linalg.norm(population[i,:] - population[l,:])
|
|
427
|
+
orgdiversity[i,l] += tmp
|
|
428
|
+
|
|
429
|
+
orgsimilarity = 1 - orgdiversity/float(m)
|
|
430
|
+
print("Organism similarity = ")
|
|
431
|
+
print(orgsimilarity)
|
|
432
|
+
return orgsimilarity
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import string
|
|
3
|
+
|
|
4
|
+
import pikaia.alg
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
rawdata3x3 = np.zeros([3,3])
|
|
8
|
+
rawdata3x3[:,:] = [[ 300, 10, 2],
|
|
9
|
+
[ 600, 5, 2],
|
|
10
|
+
[1500, 4, 1]]
|
|
11
|
+
|
|
12
|
+
rawdata10x5 = np.zeros([10,5])
|
|
13
|
+
rawdata10x5[:,:] = [[300, 10 , 2, 0, 2.5],
|
|
14
|
+
[600, 5, 2, 1, 3.0],
|
|
15
|
+
[1500, 4, 1, 2, 4.0],
|
|
16
|
+
[400, 8, 2, 0, 3.5],
|
|
17
|
+
[500, 8, 2, 1, 3.0],
|
|
18
|
+
[700, 5, 2, 1, 4.5],
|
|
19
|
+
[900, 6, 1, 1, 4.0],
|
|
20
|
+
[1100, 6, 1, 2, 3.5],
|
|
21
|
+
[1300, 5, 2, 2, 5.0],
|
|
22
|
+
[1700, 4, 1, 2, 5.0]]
|
|
23
|
+
|
|
24
|
+
class Example:
|
|
25
|
+
|
|
26
|
+
def __init__(self, inputdata, genelabels=None, orgslabels=None, labelpostfix=None):
|
|
27
|
+
self._data = inputdata
|
|
28
|
+
self._genelabelsbase = genelabels
|
|
29
|
+
if self._genelabelsbase is None:
|
|
30
|
+
self._genelabels = self._genelabelsbase
|
|
31
|
+
else:
|
|
32
|
+
self._genelabels = []
|
|
33
|
+
for k, pf in enumerate(labelpostfix):
|
|
34
|
+
# import pdb; pdb.set_trace()
|
|
35
|
+
self._genelabels.append([])
|
|
36
|
+
for j in range(0, self._data.m):
|
|
37
|
+
self._genelabels[k].append(self._genelabelsbase[j] + pf)
|
|
38
|
+
|
|
39
|
+
self._orgslabelsbase = orgslabels
|
|
40
|
+
if self._orgslabelsbase is None:
|
|
41
|
+
self._orgslabels = self._orgslabelsbase
|
|
42
|
+
else:
|
|
43
|
+
self._orgslabels = []
|
|
44
|
+
for k, pf in enumerate(labelpostfix):
|
|
45
|
+
self._orgslabels.append([])
|
|
46
|
+
for i in range(0, self._data.n):
|
|
47
|
+
self._orgslabels[k].append(self._orgslabelsbase[i] + pf)
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def exampledata(self):
|
|
51
|
+
return self._data
|
|
52
|
+
|
|
53
|
+
def get_gene_labels(self, set=0):
|
|
54
|
+
return self._genelabels[set]
|
|
55
|
+
|
|
56
|
+
def get_org_labels(self, set=0):
|
|
57
|
+
return self._orgslabels[set]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def assemble_example(specifier):
|
|
61
|
+
"""Returns a setup ready for modelling.
|
|
62
|
+
|
|
63
|
+
Returns a datastructure for Genetic Ai including
|
|
64
|
+
model and ploting parameters.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
specifier (string):
|
|
68
|
+
Giving '3x3-DomBal+AltSal' returns a small example
|
|
69
|
+
with n=3 and m=3;
|
|
70
|
+
Giving '10x5-DomBal+AltSal' returns a larger example
|
|
71
|
+
with n=10 and m=5.
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
an Example data structure.
|
|
76
|
+
"""
|
|
77
|
+
if specifier == "3x3-DomBal+AltSal":
|
|
78
|
+
|
|
79
|
+
gvfitnessrules = ["inv_percentage", "inv_percentage", "inv_percentage"]
|
|
80
|
+
inputdata = pikaia.alg.Population(rawdata3x3, gvfitnessrules)
|
|
81
|
+
|
|
82
|
+
# defining plotting labels
|
|
83
|
+
genelabels = ["gene 1 = price", "gene 2 = time", "gene 3 = stops"]
|
|
84
|
+
orgslabels = []
|
|
85
|
+
for i in range(0, inputdata.n):
|
|
86
|
+
orgslabels.append("flight " + string.ascii_uppercase[i])
|
|
87
|
+
labelpostfixes = ["(DomBal)", "(AltSel)"]
|
|
88
|
+
|
|
89
|
+
return Example(inputdata, genelabels, orgslabels, labelpostfixes)
|
|
90
|
+
|
|
91
|
+
if specifier == "10x5-DomBal+AltSal":
|
|
92
|
+
|
|
93
|
+
gvfitnessrules = ["inv_percentage", "inv_percentage", "inv_percentage",
|
|
94
|
+
"percentage", "percentage"]
|
|
95
|
+
inputdata = pikaia.alg.Population(rawdata10x5, gvfitnessrules)
|
|
96
|
+
genelabels = ["gene 1 = price", "gene 2 = time", "gene 3 = stops",
|
|
97
|
+
"gene 4 = luggage", "gene 5 = rating"]
|
|
98
|
+
orgslabels = []
|
|
99
|
+
for i in range(0, inputdata.n):
|
|
100
|
+
orgslabels.append("flight " + string.ascii_uppercase[i])
|
|
101
|
+
labelpostfixes = ["(DomBal)", "(AltSel)"]
|
|
102
|
+
|
|
103
|
+
return Example(inputdata, genelabels, orgslabels, labelpostfixes)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
else:
|
|
107
|
+
raise ValueError("Unknown example specifier")
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
import matplotlib
|
|
3
|
+
import numpy as np
|
|
4
|
+
import string
|
|
5
|
+
|
|
6
|
+
import pikaia.alg
|
|
7
|
+
|
|
8
|
+
def initialize_plotting_variables():
|
|
9
|
+
"""Initialize styles and markers for plotting.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
linestyles (list of linestyle specifiers).
|
|
14
|
+
two sets of markers (list of strings).
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
matplotlib.rcParams['pdf.fonttype'] = 42
|
|
18
|
+
matplotlib.rcParams['ps.fonttype'] = 42
|
|
19
|
+
linestyle_str = [
|
|
20
|
+
('solid', 'solid'), # Same as (0, ()) or '-'
|
|
21
|
+
('dotted', 'dotted'), # Same as ':'
|
|
22
|
+
('dashed', 'dashed'), # Same as '--'
|
|
23
|
+
('dashdot', 'dashdot')] # Same as '-.'
|
|
24
|
+
linestyle_tuple = [
|
|
25
|
+
('densely dashdotted', (0, (3, 1, 1, 1))),
|
|
26
|
+
('dashdotted', (0, (3, 5, 1, 5))),
|
|
27
|
+
('loosely dotted', (0, (1, 10))),
|
|
28
|
+
('dotted', (0, (1, 5))),
|
|
29
|
+
('densely dotted', (0, (1, 1))),
|
|
30
|
+
('long dash with offset', (5, (10, 3))),
|
|
31
|
+
('loosely dashed', (0, (5, 10))),
|
|
32
|
+
('dashed', (0, (5, 5))),
|
|
33
|
+
('densely dashed', (0, (5, 1))),
|
|
34
|
+
('loosely dashdotted', (0, (3, 10, 1, 10))),
|
|
35
|
+
('dashdotdotted', (0, (3, 5, 1, 5, 1, 5))),
|
|
36
|
+
('loosely dashdotdotted', (0, (3, 10, 1, 10, 1, 10))),
|
|
37
|
+
('densely dashdotdotted', (0, (3, 1, 1, 1, 1, 1)))]
|
|
38
|
+
linestyles = []
|
|
39
|
+
for i, (name, linestyle) in enumerate(linestyle_str):
|
|
40
|
+
linestyles.append(linestyle)
|
|
41
|
+
for i, (name, linestyle) in enumerate(linestyle_tuple):
|
|
42
|
+
linestyles.append(linestyle)
|
|
43
|
+
markerstylesSet1 = ["o","^","s", ">", "p", "X", ".", "*", "1", "3"]
|
|
44
|
+
markerstylesSet2 = ["x","v", "D","<", "P", "h", "8", "d", "2", "4"]
|
|
45
|
+
return linestyles, markerstylesSet1, markerstylesSet2
|
|
46
|
+
|
|
47
|
+
def plot_gene_fitness(modellist, figurenr, show=True, savename=None,
|
|
48
|
+
fontsize=12, linewidth=3):
|
|
49
|
+
"""Plots the gene fitness values over iterations.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
modellist (List of geneticai models):
|
|
53
|
+
List of geneticai models to be plotted.
|
|
54
|
+
figurenr (integer):
|
|
55
|
+
The figure window specifier.
|
|
56
|
+
show (boolean):
|
|
57
|
+
Whether the finished plots are shown.
|
|
58
|
+
savename (String):
|
|
59
|
+
Whether the finished plots are saved as png and pdf.
|
|
60
|
+
fontsize (integer):
|
|
61
|
+
Fontsizes in the plots
|
|
62
|
+
linewidth (integer):
|
|
63
|
+
Linewidths in the plots
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
plt.figure(figurenr)
|
|
69
|
+
plt.rcParams.update({'font.size': fontsize})
|
|
70
|
+
|
|
71
|
+
maxpiter = 0
|
|
72
|
+
for model in modellist:
|
|
73
|
+
if model._iterESE > maxpiter:
|
|
74
|
+
maxpiter = model._iterESE
|
|
75
|
+
|
|
76
|
+
if maxpiter > 20:
|
|
77
|
+
inc = round(maxpiter/10)
|
|
78
|
+
plt.xticks(range(0,maxpiter, inc))
|
|
79
|
+
else:
|
|
80
|
+
inc = 5
|
|
81
|
+
plt.xticks(range(0,maxpiter))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
for model in modellist:
|
|
85
|
+
n = model._gps.shape[1]
|
|
86
|
+
for j in range(0, n):
|
|
87
|
+
plt.plot(range(0, model._iterESE+1), model._gps[:model._iterESE+1,j],
|
|
88
|
+
label=model._genelabels[j], linestyle=model._linestyles[j],
|
|
89
|
+
marker=model._markerstyles[j], markersize=8, markevery=inc,
|
|
90
|
+
lw=linewidth)
|
|
91
|
+
plt.ylabel('gene fitness [%]')
|
|
92
|
+
plt.xlabel('iterations')
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
plt.xlim(0,maxpiter)
|
|
96
|
+
plt.legend(handlelength=5, fontsize=10)
|
|
97
|
+
if savename is not None:
|
|
98
|
+
plt.savefig(savename + ".pdf", format="pdf", bbox_inches="tight")
|
|
99
|
+
plt.savefig(savename + ".png", format="png", bbox_inches="tight")
|
|
100
|
+
if show:
|
|
101
|
+
plt.show()
|
|
102
|
+
|
|
103
|
+
def plot_organism_fitness(modellist, figurenr, maxitershown, show=True, savename=None,
|
|
104
|
+
fontsize=12, linewidth=3):
|
|
105
|
+
|
|
106
|
+
"""Plots the organism fitness values over iterations.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
modellist (List of geneticai models):
|
|
110
|
+
List of geneticai models to be plotted.
|
|
111
|
+
figurenr (integer):
|
|
112
|
+
The figure window specifier.
|
|
113
|
+
maxitershown (integer):
|
|
114
|
+
The maximum iteration shown in the plot.
|
|
115
|
+
show (boolean):
|
|
116
|
+
Whether the finished plots are shown.
|
|
117
|
+
savename (String):
|
|
118
|
+
Whether the finished plots are saved as png and pdf.
|
|
119
|
+
fontsize (integer):
|
|
120
|
+
Fontsizes in the plots
|
|
121
|
+
linewidth (integer):
|
|
122
|
+
Linewidths in the plots
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
"""
|
|
126
|
+
plt.figure(figurenr)
|
|
127
|
+
plt.rcParams.update({'font.size': fontsize})
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
maxpiter = 0
|
|
131
|
+
for model in modellist:
|
|
132
|
+
if model._iterESE > maxpiter:
|
|
133
|
+
maxpiter = model._iterESE
|
|
134
|
+
|
|
135
|
+
if maxitershown is not None and maxitershown < maxpiter:
|
|
136
|
+
maxpiter = maxitershown
|
|
137
|
+
|
|
138
|
+
if maxpiter > 20:
|
|
139
|
+
inc = round(maxpiter/10)
|
|
140
|
+
plt.xticks(range(0,maxpiter, inc))
|
|
141
|
+
else:
|
|
142
|
+
inc = 5
|
|
143
|
+
plt.xticks(range(0,maxpiter))
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
for model in modellist:
|
|
147
|
+
n = model._ofs.shape[1]
|
|
148
|
+
for j in range(0, n):
|
|
149
|
+
plt.plot(range(0, model._iterESE+1), model._ofs[:model._iterESE+1,j],
|
|
150
|
+
label=model._orgslabels[j], linestyle=model._linestyles[j],
|
|
151
|
+
marker=model._markerstyles[j], markersize=8, markevery=inc,
|
|
152
|
+
lw=linewidth)
|
|
153
|
+
plt.ylabel('organism fitness [%]')
|
|
154
|
+
plt.xlabel('iterations')
|
|
155
|
+
|
|
156
|
+
plt.xlim(0,maxpiter)
|
|
157
|
+
plt.legend(handlelength=5, fontsize=10)
|
|
158
|
+
if savename is not None:
|
|
159
|
+
plt.savefig(savename + ".pdf", format="pdf", bbox_inches="tight")
|
|
160
|
+
plt.savefig(savename + ".png", format="png", bbox_inches="tight")
|
|
161
|
+
if show:
|
|
162
|
+
plt.show()
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
import pikaia as gai
|
|
6
|
+
import pikaia.alg
|
|
7
|
+
|
|
8
|
+
class Search:
|
|
9
|
+
"""
|
|
10
|
+
Class that performs a string-based search over a data matrix
|
|
11
|
+
using Genetic AI.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, input_data, orgs_labels, gene_labels):
|
|
15
|
+
self._data = input_data
|
|
16
|
+
self._gene_labels = gene_labels
|
|
17
|
+
self._orgs_labels = orgs_labels
|
|
18
|
+
|
|
19
|
+
def search_request(self, query: str, top_k: int = None, silent=False) -> list:
|
|
20
|
+
"""
|
|
21
|
+
Performs a search request on the data given a query string.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
query (str): a string containing comma-separated keywords.
|
|
25
|
+
top_k (int): how many top-ranked organisms to return.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Tuple[list, list] containing the organism and gene fitness
|
|
29
|
+
values at the end of the simulation.
|
|
30
|
+
"""
|
|
31
|
+
# Process query
|
|
32
|
+
split_query = [f.strip().lower() for f in query.split(",")]
|
|
33
|
+
query_features = [f for f in split_query if f in self._gene_labels]
|
|
34
|
+
query_feature_ids = [self._gene_labels.index(f) for f in query_features]
|
|
35
|
+
query_year = next((int(f) for f in split_query if is_year(f)), 0)
|
|
36
|
+
|
|
37
|
+
# For testing purposes set row cutoff
|
|
38
|
+
cutoff = None
|
|
39
|
+
if cutoff is None:
|
|
40
|
+
cutoff = self._data.shape[0]
|
|
41
|
+
|
|
42
|
+
# Get the relevant colum for each feature and create a new matrix
|
|
43
|
+
data_subset = self._data[:cutoff, query_feature_ids]
|
|
44
|
+
|
|
45
|
+
# Use percentage rule for all features except year
|
|
46
|
+
gvfitnessrules = ["percentage"] * len(query_features)
|
|
47
|
+
|
|
48
|
+
# Get relevant columns from matrix
|
|
49
|
+
if query_year > 0:
|
|
50
|
+
# Convert year column -> calculate the distance to the year in the query
|
|
51
|
+
year_col = abs(self._data[:cutoff, self._gene_labels.index("year")] - query_year)
|
|
52
|
+
|
|
53
|
+
# Add to the data_subset
|
|
54
|
+
data_subset = np.column_stack((data_subset, year_col))
|
|
55
|
+
|
|
56
|
+
# Special handling for year
|
|
57
|
+
gvfitnessrules += ["inv_percentage"]
|
|
58
|
+
|
|
59
|
+
# Print logs
|
|
60
|
+
print("Selected features:", query_features, f"(ids: {query_feature_ids})")
|
|
61
|
+
print("Number of non-zero values per selected feature:")
|
|
62
|
+
for f_id, f in zip(query_feature_ids, query_features):
|
|
63
|
+
print(f" - {f}:", len([w for w in self._data[:, f_id] if w != 0]))
|
|
64
|
+
|
|
65
|
+
print(f"Subset of data matrix (shape: {data_subset.shape})\n", data_subset)
|
|
66
|
+
|
|
67
|
+
# Convert raw data to population
|
|
68
|
+
population = pikaia.alg.Population(data_subset, gvfitnessrules)
|
|
69
|
+
|
|
70
|
+
strategy = ["GS Dominant", "OS Balanced"]
|
|
71
|
+
# Use this for AltSel strategies (reduce maxiter for performance)
|
|
72
|
+
# strategy = ["GS Altruistic", "OS Selfish"]
|
|
73
|
+
|
|
74
|
+
# Initialize model
|
|
75
|
+
model = gai.alg.Model(population, strategy)
|
|
76
|
+
|
|
77
|
+
# Start with a uniform distribution
|
|
78
|
+
n_features = data_subset.shape[1]
|
|
79
|
+
initialgenefitness = [1 / n_features] * n_features
|
|
80
|
+
|
|
81
|
+
# Run simulation
|
|
82
|
+
e = 0.00005
|
|
83
|
+
gene_fitness, fitness = model.complete_run(initialgenefitness, maxiteration=100,
|
|
84
|
+
epsilon=e, silent=silent)
|
|
85
|
+
|
|
86
|
+
# Function to get all non-zero features for a selected organism
|
|
87
|
+
def get_features(organism):
|
|
88
|
+
org_index = self._orgs_labels.index(organism)
|
|
89
|
+
return [f_name for f_weight, f_name in zip(self._data[org_index,:], self._gene_labels)
|
|
90
|
+
if f_weight != 0]
|
|
91
|
+
|
|
92
|
+
# Function to get the feature values for a selected organism
|
|
93
|
+
def get_feature_values(organism):
|
|
94
|
+
org_index = self._orgs_labels.index(organism)
|
|
95
|
+
return [(f_name, f_weight)
|
|
96
|
+
for f_weight, f_name in zip(data_subset[org_index,:], query_features)]
|
|
97
|
+
|
|
98
|
+
# Sort organisms with their corresponding fitness value
|
|
99
|
+
org_fitness = list(sorted(
|
|
100
|
+
[(org_label, fit, get_feature_values(org_label))
|
|
101
|
+
for org_label, fit in zip(self._orgs_labels, fitness)],
|
|
102
|
+
key=lambda x: x[1],
|
|
103
|
+
reverse=True
|
|
104
|
+
))
|
|
105
|
+
|
|
106
|
+
gene_fitness_labels = [(l, f) for l, f in zip(query_features, gene_fitness)]
|
|
107
|
+
# Return the top_k fittest organisms
|
|
108
|
+
return org_fitness[:top_k], gene_fitness_labels
|
|
109
|
+
|
|
110
|
+
def is_year(s):
|
|
111
|
+
"""Checks if a string is a year number."""
|
|
112
|
+
try:
|
|
113
|
+
year = int(s)
|
|
114
|
+
except ValueError:
|
|
115
|
+
return False
|
|
116
|
+
return year >= 1890 and year <= datetime.date.today().year + 1
|