Skip to content

Commit

Permalink
Merge pull request #22 from BorjaEst/dev
Browse files Browse the repository at this point in the history
fix EmptyInterface and minor changes
  • Loading branch information
BorjaEst authored Feb 8, 2023
2 parents d33d62e + aef6b83 commit 14c48b6
Show file tree
Hide file tree
Showing 11 changed files with 33 additions and 18 deletions.
3 changes: 0 additions & 3 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
// connected. This is typically a file mount in .devcontainer/docker-compose.yml
"workspaceFolder": "/workspace",
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "lts"
},
"ghcr.io/devcontainers/features/common-utils:1": { },
"ghcr.io/devcontainers/features/git:1": { },
"ghcr.io/devcontainers/features/nvidia-cuda:1": { },
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,6 @@ dmypy.json

# Pyre type checker
.pyre/

# Whiteboard files
whiteboard.py
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,13 @@ instance of the defined class. You can use the init arguments `cache` and
```py
from genopy import fitness

class MyFitness1(fitness.FitnessModel):
class MyFitness(fitness.FitnessModel):
def score(self, phenotype):
return phenotype.chromosome.count(1)
x1 = phenotype.chromosome_1.count(1)
x2 = phenotype.chromosome_2.count(0)
return x1 + x2

fx = MyFitness1(cache=True, parallel=True)
fx = MyFitness(cache=True, parallel=True)
```
> You can additionally define `setup` as method to execute once at the begining
of each generation before phenotypes are evaluated.
Expand Down Expand Up @@ -102,7 +104,7 @@ instantiating the experiment to store all phenotypes during the execution.
import gevopy as ea

experiment = ea.Experiment(
fitness=MyFitness(cached=True, schedule="synchronous"),
fitness=MyFitness(cache=True, schedule="synchronous"),
algorithm=MyAlgorithm(survival_rate=0.2),
)

Expand All @@ -128,3 +130,13 @@ Storing relationships at the record level makes sense in genotype
relationships as it provides index-free adjacency.
Graph traversal operations such 'genealogy tree' or certain matches can
be performed with no index lookups leading to much better performance.

### Why pydantic instead of dataclass?
Pydantic supports validation of fields during and after the
initialization process and makes parsing easier.
Parsing is a relevant step if you are planing to save your
phenotypes into the connected database.

### Limitations
Collections containing collections can not be stored in properties.
Property values can only be of primitive types or arrays in Neo4J Cypher queries.
2 changes: 1 addition & 1 deletion examples/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def score(self, phenotype):


# ------------------------------------------------------------------
# Fitness ----------------------------------------------------------
# Random -----------------------------------------------------------
# This fitness object scores each phenotypes completelly random.
class Random(FitnessModel):
"""Fitness model assigns a random score between 0-1"""
Expand Down
4 changes: 2 additions & 2 deletions examples/genotypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@


# ------------------------------------------------------------------
# Genotype ---------------------------------------------------------
# Bacteria ---------------------------------------------------------
# This genotypes are known to have asexual reproduction and with a
# unique chromosome. In this example values can be only between 0 and 1
# See https://pydantic-docs.helpmanual.io/usage/models/
Expand All @@ -19,7 +19,7 @@ class Bacteria(GenotypeModel):


# ------------------------------------------------------------------
# Genotype ---------------------------------------------------------
# JackJumper -------------------------------------------------------
# Although males are know to be composed by haploids, this is a good
# example of how to design a chromosome where values can take more
# values than 1 or 0 (0-3) due to that each value in the chromosome
Expand Down
2 changes: 1 addition & 1 deletion src/gevopy/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.0
1.0.1
7 changes: 4 additions & 3 deletions src/gevopy/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ def add_phenotypes(cls, _container, phenotypes):
"""
phenotypes = list(phenotypes)
cls.logger.debug('Adding phenotypes %s', phenotypes)
return list(str(p.id) for p in phenotypes)
return list(p['id'] for p in phenotypes)

@classmethod
def get_phenotypes(cls, _container, ids):
Expand Down Expand Up @@ -409,8 +409,9 @@ def get_phenotypes(tx, ids):
"WITH x, e, collect(y.id) as p "
"RETURN x{.*, .score, experiment:e.name, parents:p } "
)
result = tx.run(query, phenotypes_ids=ids)
return [dict(record["x"]) for record in result]
result = [dict(r["x"]) for r in tx.run(query, phenotypes_ids=ids)]
result.sort(key=lambda r: ids.index(r['id']))
return result


@neo4j.unit_of_work(timeout=config['timeout'])
Expand Down
1 change: 1 addition & 0 deletions src/gevopy/experiments.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ def run(self, session):
try:
logger.info("Start of evolutionary experiment execution")
session.eval_phenotypes(fitness, save=True) # Evaluate first pop
self.halloffame.update(session.get_phenotypes())
while not self.completed:
self.generation += 1 # Increase generation index
session.generate_offspring(algorithm, save=False)
Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def driver_kwds(request):
return request.param


@fixture(scope="session", params=["Neo4jInterface"])
@fixture(scope="session", params=["Neo4jInterface", "EmptyInterface"])
def db_interface(request, driver_kwds):
"""Fixture to return the experiment interface for the database"""
match request.param:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_requirements/test_experiments.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def test_generation_stop(self, execution, max_generation):
def test_score_stop(self, execution, max_score):
"""Test execution stops when maximum score is reached"""
assert execution.best_score >= max_score
assert execution.generation > 0
assert execution.generation >= 0

def test_attr_generation(self, execution):
"""Test generation attr returns int after first execution"""
Expand Down
5 changes: 3 additions & 2 deletions tests/test_utilities/test_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import uuid

from pytest import fixture
from pytest import fixture, mark


@fixture(scope="module", autouse=True)
Expand Down Expand Up @@ -52,8 +52,9 @@ def ids(population):
return [str(ph.id) for ph in population]


@mark.parametrize("db_interface", ["Neo4jInterface"], indirect=True)
def test_rwd_phenotypes(session, phenotypes, ids):
"""Test write, read and delete of phenotypes in db"""
assert ids == session.add_phenotypes(phenotypes)
assert phenotypes == session.get_phenotypes(ids)
assert ids == session.del_phenotypes(ids)
assert all([id in ids for id in session.del_phenotypes(ids)])

0 comments on commit 14c48b6

Please sign in to comment.