Skip to content

Commit

Permalink
Remove logging (#140)
Browse files Browse the repository at this point in the history
* removed logging

* fixed logging removal!

* fixed that step thing

* fixed logging example: 00ps
  • Loading branch information
koaning committed Aug 29, 2019
1 parent 61467fd commit 6bd50e3
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 61 deletions.
4 changes: 2 additions & 2 deletions evol/evolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def repeat(self, evolution: 'Evolution', n: int = 1, name: Optional[str] = None)
return self._add_step(RepeatStep(name=name, evolution=evolution, n=n))

def callback(self, callback_function: Callable[..., Any],
every: int = 1, name: Optional[str] = None) -> 'Evolution':
every: int = 1, name: Optional[str] = None, **kwargs) -> 'Evolution':
"""Call a function as a step in this evolution.
This will call the provided function with the population as argument.
Expand All @@ -213,7 +213,7 @@ def callback(self, callback_function: Callable[..., Any],
:param name: Name of the callback step.
:return: self
"""
return self._add_step(CallbackStep(name=name, every=every, callback_function=callback_function))
return self._add_step(CallbackStep(name=name, every=every, callback_function=callback_function, **kwargs))

def _add_step(self, step):
result = copy(self)
Expand Down
8 changes: 8 additions & 0 deletions evol/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,19 @@ def __init__(self, target=None, stdout=False, fmt='%(asctime)s,%(message)s'):
self.logger.addHandler(stream_handler)
self.logger.setLevel(level=logging.INFO)

@staticmethod
def check_population(population):
if any([i.fitness is None for i in population]):
# TODO: add a custom error type for PopulationNotEvaluated
raise RuntimeError("Population is not evaluated before logging!")

def log(self, population, **kwargs):
"""
The logger method of the Logger object determines what will be logged.
:param population: `evol.Population` object
:return: nothing, it merely logs to a file and perhaps stdout
"""
self.check_population(population)
values = ','.join([str(item) for item in kwargs.values()])
if values != '':
values = f',{values}'
Expand All @@ -59,6 +66,7 @@ class SummaryLogger(BaseLogger):
"""

def log(self, population, **kwargs):
self.check_population(population)
values = ','.join([str(item) for item in kwargs.values()])
if values != '':
values = f',{values}'
Expand Down
25 changes: 0 additions & 25 deletions evol/population.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from evol.conditions import Condition
from evol.exceptions import StopEvolution
from evol.utils import offspring_generator, select_arguments
from evol.logger import BaseLogger
from evol.serialization import SimpleSerializer

if TYPE_CHECKING:
Expand All @@ -33,7 +32,6 @@ def __init__(self,
checkpoint_target: Optional[str] = None,
concurrent_workers: Optional[int] = 1,
maximize: bool = True,
logger=None,
generation: int = 0,
intended_size: Optional[int] = None,
serializer=None):
Expand All @@ -44,7 +42,6 @@ def __init__(self,
self.id = str(uuid4())[:6]
self.individuals = [Individual(chromosome=chromosome) for chromosome in chromosomes]
self.intended_size = intended_size or len(self.individuals)
self.logger = logger or BaseLogger()
self.maximize = maximize
self.serializer = serializer or SimpleSerializer(target=checkpoint_target)
self.pool = None if concurrent_workers == 1 else Pool(concurrent_workers)
Expand Down Expand Up @@ -208,18 +205,6 @@ def mutate(self,
individual.mutate(mutate_function, probability=probability, **kwargs)
return self

def log(self, **kwargs) -> 'BasePopulation':
"""
Logs a population. If a Population object was initialized with a logger
object then you may specify how logging is handled. The base logging
operation just logs to standard out.
:return: self
"""
self.evaluate(lazy=True)
self.logger.log(population=self, **kwargs)
return self

def map(self, func: Callable[..., Individual], **kwargs) -> 'BasePopulation':
"""Apply the provided function to each individual in the population.
Expand Down Expand Up @@ -308,8 +293,6 @@ class Population(BasePopulation):
:param eval_function: Function that reduces a chromosome to a fitness.
:param maximize: If True, fitness will be maximized, otherwise minimized.
Defaults to True.
:param logger: Logger object for the Population. If None, a new BaseLogger
is created. Defaults to None.
:param generation: Generation of the Population. This is incremented after
each breed call. Defaults to 0.
:param intended_size: Intended size of the Population. The population will
Expand All @@ -327,7 +310,6 @@ def __init__(self,
chromosomes: Iterable,
eval_function: Callable[..., float],
maximize: bool = True,
logger=None,
generation: int = 0,
intended_size: Optional[int] = None,
checkpoint_target: Optional[str] = None,
Expand All @@ -338,7 +320,6 @@ def __init__(self,
checkpoint_target=checkpoint_target,
concurrent_workers=concurrent_workers,
maximize=maximize,
logger=logger,
generation=generation,
intended_size=intended_size,
serializer=serializer)
Expand All @@ -349,7 +330,6 @@ def __copy__(self):
maximize=self.maximize,
serializer=self.serializer,
intended_size=self.intended_size,
logger=self.logger,
generation=self.generation,
concurrent_workers=1) # Prevent new pool from being made
result.individuals = [copy(individual) for individual in self.individuals]
Expand Down Expand Up @@ -462,8 +442,6 @@ class ContestPopulation(BasePopulation):
:param contests_per_round: Minimum number of contests each individual
takes part in for each evaluation round. The actual number of contests
per round is a multiple of individuals_per_contest. Defaults to 10.
:param logger: Logger object for the Population. If None, a new BaseLogger
is created. Defaults to None.
:param generation: Generation of the Population. This is incremented after
echo survive call. Defaults to 0.
:param intended_size: Intended size of the Population. The population will
Expand All @@ -484,7 +462,6 @@ def __init__(self,
maximize: bool = True,
individuals_per_contest=2,
contests_per_round=10,
logger=None,
generation: int = 0,
intended_size: Optional[int] = None,
checkpoint_target: Optional[int] = None,
Expand All @@ -493,7 +470,6 @@ def __init__(self,
super().__init__(chromosomes=chromosomes,
eval_function=eval_function,
maximize=maximize,
logger=logger,
generation=generation,
intended_size=intended_size,
checkpoint_target=checkpoint_target,
Expand All @@ -510,7 +486,6 @@ def __copy__(self):
individuals_per_contest=self.individuals_per_contest,
serializer=self.serializer,
intended_size=self.intended_size,
logger=self.logger,
generation=self.generation,
concurrent_workers=1)
result.individuals = [copy(individual) for individual in self.individuals]
Expand Down
8 changes: 4 additions & 4 deletions examples/simple_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,22 @@ def add_noise(chromosome, sigma):
return new_x, new_y


logger = BaseLogger(target="/tmp/evol.log")
pop = Population(chromosomes=[random_start() for _ in range(200)],
eval_function=func_to_optimise,
maximize=True, concurrent_workers=2,
logger=BaseLogger(target="/tmp/evol.log"))
maximize=True, concurrent_workers=2)

evo1 = (Evolution()
.survive(fraction=0.1)
.breed(parent_picker=pick_random_parents, combiner=make_child)
.mutate(mutate_function=add_noise, sigma=0.2)
.log())
.callback(logger.log))

evo2 = (Evolution()
.survive(n=10)
.breed(parent_picker=pick_random_parents, combiner=make_child)
.mutate(mutate_function=add_noise, sigma=0.1)
.log())
.callback(logger.log))

evo3 = (Evolution()
.repeat(evo1, n=20)
Expand Down
71 changes: 41 additions & 30 deletions tests/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,52 +10,56 @@ class TestLoggerSimple:
def test_baselogger_write_file_no_stdout(self, tmpdir, capsys, simple_chromosomes, simple_evaluation_function):
log_file = tmpdir.join('log.txt')
logger = BaseLogger(target=log_file, stdout=False)
pop = Population(chromosomes=simple_chromosomes, eval_function=simple_evaluation_function, logger=logger)
pop = Population(chromosomes=simple_chromosomes, eval_function=simple_evaluation_function)
# we should see that a file was created with an appropriate number of rows
pop.log()
pop.evaluate()
logger.log(pop)
with open(log_file, "r") as f:
assert len(f.readlines()) == len(simple_chromosomes)
# we should see that a file was created with an appropriate number of rows
pop.log()
logger.log(pop)
with open(log_file, "r") as f:
assert len(f.readlines()) == (2 * len(simple_chromosomes))
read_stdout = [line for line in capsys.readouterr().out.split('\n') if line != '']
# there should be nothing printed
assert len(read_stdout) == 0

def test_baselogger_can_write_to_stdout(self, capsys, simple_chromosomes, simple_evaluation_function):
def test_baselogger_can_write_to_stdout(self, capsys, simple_chromosomes):
logger = BaseLogger(target=None, stdout=True)

pop = Population(chromosomes=simple_chromosomes,
eval_function=lambda x: x,
logger=BaseLogger(target=None, stdout=True))
pop.log()
eval_function=lambda x: x)
pop.evaluate()
logger.log(pop)
read_stdout = [line for line in capsys.readouterr().out.split('\n') if line != '']
assert len(read_stdout) == len(pop)

def test_baselogger_can_accept_kwargs(self, tmpdir, simple_chromosomes, simple_evaluation_function):
log_file = tmpdir.join('log.txt')
logger = BaseLogger(target=log_file, stdout=False)
pop = Population(chromosomes=simple_chromosomes, eval_function=simple_evaluation_function, logger=logger)
pop = Population(chromosomes=simple_chromosomes, eval_function=simple_evaluation_function)
pop.evaluate()
# we should see that a file was created with an appropriate number of rows
pop.log(foo="bar")
logger.log(pop, foo="bar")
with open(log_file, "r") as f:
assert len(f.readlines()) == len(simple_chromosomes)
assert all(["bar" in l for l in f.readlines()])
# we should see that a file was created with an appropriate number of rows
pop.log(foo="meh")
logger.log(pop, foo="meh")
with open(log_file, "r") as f:
assert len(f.readlines()) == (2 * len(simple_chromosomes))
assert all(['meh' in l for l in f.readlines()[-10:]])

def test_baselogger_works_via_evolution(self, tmpdir, capsys):
def test_baselogger_works_via_evolution_callback(self, tmpdir, capsys):
log_file = tmpdir.join('log.txt')
logger = BaseLogger(target=log_file, stdout=True)
pop = Population(chromosomes=range(10), eval_function=lambda x: x, logger=logger)
pop = Population(chromosomes=range(10), eval_function=lambda x: x)
evo = (Evolution()
.survive(fraction=0.5)
.breed(parent_picker=pick_random,
combiner=lambda mom, dad: (mom + dad) / 2 + (random.random() - 0.5),
n_parents=2)
.log(foo='bar'))
.callback(logger.log, foo='bar'))
pop.evolve(evolution=evo, n=2)
# check characteristics of the file
with open(log_file, "r") as f:
Expand All @@ -69,37 +73,41 @@ def test_baselogger_works_via_evolution(self, tmpdir, capsys):
assert len(read_stdout) == 2 * len(pop)
assert all(['bar' in row for row in read_stdout])

def test_summarylogger_write_file_mo_stdout(self, tmpdir, capsys, simple_chromosomes, simple_evaluation_function):
def test_summarylogger_write_file_mo_stdout(self, tmpdir, capsys):
log_file = tmpdir.join('log.txt')
logger = SummaryLogger(target=log_file, stdout=False)
pop = Population(chromosomes=range(10), eval_function=lambda x: x, logger=logger)
pop = Population(chromosomes=range(10), eval_function=lambda x: x)
# we should see that a file was created with an appropriate number of rows
pop.log()
pop.evaluate()
logger.log(pop)
with open(log_file, "r") as f:
assert len(f.readlines()) == 1
# we should see that a file was created with an appropriate number of rows
pop.log()
logger.log(pop)
with open(log_file, "r") as f:
assert len(f.readlines()) == 2
read_stdout = [line for line in capsys.readouterr().out.split('\n') if line != '']
# there should be nothing printed
assert len(read_stdout) == 0

def test_summarylogger_can_write_to_stdout(self, capsys, simple_chromosomes, simple_evaluation_function):
logger = SummaryLogger(target=None, stdout=True)
pop = Population(chromosomes=range(10),
eval_function=lambda x: x,
logger=SummaryLogger(target=None, stdout=True))
pop.log().log()
eval_function=lambda x: x)
pop.evaluate()
logger.log(pop)
logger.log(pop)
read_stdout = [line for line in capsys.readouterr().out.split('\n') if line != '']
assert len(read_stdout) == 2

def test_summary_logger_can_accept_kwargs(self, tmpdir, simple_chromosomes, simple_evaluation_function):
log_file = tmpdir.join('log.txt')
logger = SummaryLogger(target=log_file, stdout=False)
pop = Population(chromosomes=simple_chromosomes,
eval_function=simple_evaluation_function, logger=logger)
eval_function=simple_evaluation_function)
pop.evaluate()
# lets make a first simple log
pop.log(foo="bar", buzz="meh")
logger.log(pop, foo="bar", buzz="meh")
with open(log_file, "r") as f:
read_lines = f.readlines()
assert len(read_lines) == 1
Expand All @@ -109,7 +117,7 @@ def test_summary_logger_can_accept_kwargs(self, tmpdir, simple_chromosomes, simp
last_entry = first_line.split(",")
assert len(last_entry) == 6
# lets log another one
pop.log(buzz="moh")
logger.log(pop, buzz="moh")
with open(log_file, "r") as f:
read_lines = f.readlines()
assert len(read_lines) == 2
Expand All @@ -121,13 +129,14 @@ def test_summary_logger_can_accept_kwargs(self, tmpdir, simple_chromosomes, simp
def test_summarylogger_works_via_evolution(self, tmpdir, capsys):
log_file = tmpdir.join('log.txt')
logger = SummaryLogger(target=log_file, stdout=True)
pop = Population(chromosomes=list(range(10)), eval_function=lambda x: x, logger=logger)
pop = Population(chromosomes=list(range(10)), eval_function=lambda x: x)
evo = (Evolution()
.survive(fraction=0.5)
.breed(parent_picker=pick_random,
combiner=lambda mom, dad: (mom + dad) / 2 + (random.random() - 0.5),
n_parents=2)
.log(foo='bar'))
.evaluate()
.callback(logger.log, foo='bar'))
pop.evolve(evolution=evo, n=5)
# check characteristics of the file
with open(log_file, "r") as f:
Expand All @@ -144,14 +153,15 @@ def test_summarylogger_works_via_evolution(self, tmpdir, capsys):
def test_two_populations_can_use_same_logger(self, tmpdir, capsys):
log_file = tmpdir.join('log.txt')
logger = SummaryLogger(target=log_file, stdout=True)
pop1 = Population(chromosomes=list(range(10)), eval_function=lambda x: x, logger=logger)
pop2 = Population(chromosomes=list(range(10)), eval_function=lambda x: x, logger=logger)
pop1 = Population(chromosomes=list(range(10)), eval_function=lambda x: x)
pop2 = Population(chromosomes=list(range(10)), eval_function=lambda x: x)
evo = (Evolution()
.survive(fraction=0.5)
.breed(parent_picker=pick_random,
combiner=lambda mom, dad: (mom + dad) + 1,
n_parents=2)
.log(foo="dino"))
.evaluate()
.callback(logger.log, foo="dino"))
pop1.evolve(evolution=evo, n=5)
pop2.evolve(evolution=evo, n=5)
# two evolutions have now been applied, lets check the output!
Expand All @@ -170,13 +180,14 @@ def test_two_populations_can_use_same_logger(self, tmpdir, capsys):
def test_every_mechanic_in_evolution_log(self, tmpdir, capsys):
log_file = tmpdir.join('log.txt')
logger = SummaryLogger(target=log_file, stdout=True)
pop = Population(chromosomes=list(range(10)), eval_function=lambda x: x, logger=logger)
pop = Population(chromosomes=list(range(10)), eval_function=lambda x: x)
evo = (Evolution()
.survive(fraction=0.5)
.breed(parent_picker=pick_random,
combiner=lambda mom, dad: (mom + dad) + 1,
n_parents=2)
.log(every=2))
.evaluate()
.callback(logger.log, every=2))
pop.evolve(evolution=evo, n=100)
with open(log_file, "r") as f:
read_file = [item.replace("\n", "") for item in f.readlines()]
Expand Down

0 comments on commit 6bd50e3

Please sign in to comment.