Skip to content

Commit

Permalink
Summertime release (0.8a1.dev16)
Browse files Browse the repository at this point in the history
  • Loading branch information
squillero committed Aug 25, 2023
1 parent 91f46ed commit e838c60
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 37 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![License: Apache 2.0](https://img.shields.io/badge/license-apache--2.0-green.svg)](https://opensource.org/licenses/Apache-2.0)
[![Status: Actrive](https://img.shields.io/badge/status-active-brightgreen.svg)](https://github.com/cad-polito-it/byron)
![Language: Python](https://img.shields.io/badge/language-python-blue.svg)
![Version: 0.1.dev2](https://img.shields.io/badge/version-0.8a1.dev15-orange.svg)
![Version: 0.1.dev2](https://img.shields.io/badge/version-0.8a1.dev16-orange.svg)
![Codename: Don Juan](https://img.shields.io/badge/codename-Don_Juan-pink.svg)
[![Documentation Status](https://readthedocs.org/projects/byron/badge/?version=pre-alpha)](https://byron.readthedocs.io/en/pre-alpha/?badge=pre-alpha)

Expand Down
2 changes: 1 addition & 1 deletion byron/classes/individual.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def fitness(self, value: FitnessABC) -> None:
value << i.fitness or not value.is_distinguishable(i.fitness) for i in self.lineage.parents
):
self._lineage.operator.stats.failures += 1
logger.debug(f"Individual: Fitness of {self} is {value}")
logger.debug(f"Individual: Fitness of {self}/{self.lineage} is {value}")

@property
def as_message(self) -> list[int]:
Expand Down
2 changes: 1 addition & 1 deletion byron/classes/selement.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def _is_valid_debug(self, node_ref: 'NodeReference') -> None:
check_result = True
for f in self.__class__.NODE_CHECKS:
if not f(node_ref):
logger.info(f"{PARANOIA_SYSTEM_ERROR}: Failed check on {node_ref} ({f})")
logger.info(f"is_valid: Failed check on {node_ref}: {f}")
check_result = False
return check_result

Expand Down
2 changes: 1 addition & 1 deletion byron/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def mutate(parameter: ParameterABC, /, strength: float) -> None:
or counter < 100
or strength < 0.01
or performance_warning(
f"Failed to mutate {parameter.__class__.__name__} with strength {strength} ({counter:,} failed attempts)",
f"Failed to mutate {parameter!r} with strength {strength} ({counter:,} failed attempts)",
stacklevel_offset=2,
)
)
2 changes: 1 addition & 1 deletion byron/global_symbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
from collections import defaultdict
import multiprocessing

__version__ = "0.8a1.dev15"
__version__ = "0.8a1.dev16"
__date__ = "25-08-2023"
__codename__ = "Don Juan"
__author__ = "Giovanni Squillero and Alberto Tonda"
Expand Down
83 changes: 63 additions & 20 deletions byron/operators/single_node_crossovers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
# v1 / August 2023 / Squillero (GX)

from copy import deepcopy
from collections import defaultdict

from .ea_tools import *
from byron.classes import *
Expand All @@ -35,50 +36,92 @@
from byron.tools.graph import *
from byron.user_messages import *

# unfussy vs. chosy

def _generic_node_crossover(
parent1: Individual, parent2: Individual, *, choosy: bool = False, only_targets: bool = False, link_type: str
):
# assert parent1.run_paranoia_checks()
# assert parent2.run_paranoia_checks()

common_selements_raw = group_selements([parent1, parent2], only_targets=only_targets, choosy=choosy)
common_selements = defaultdict(lambda: defaultdict(list))
for path, elements in common_selements_raw.items():
if len(elements) < 2:
continue
for ind, nodes in elements.items():
plausible_nodes = [
n for n in nodes if link_type in set(t for u, v, t in ind.genome.in_edges(n, data='_type'))
]
if not plausible_nodes:
continue
common_selements[path][ind] = plausible_nodes
if len(common_selements[path]) != 2:
del common_selements[path]

def _generic_node_crossover(parent1: Individual, parent2: Individual, link_type: str):
assert parent1.run_paranoia_checks()
assert parent2.run_paranoia_checks()
common_selements = {k: v for k, v in group_selements([parent1, parent2], only_targets=True).items() if len(v) == 2}
if not common_selements:
raise ByronOperatorFailure

target = rrandom.choice(list(common_selements.keys()))
node1 = rrandom.choice(common_selements[target][parent1])
node2 = rrandom.choice(common_selements[target][parent2])
new_genome = nx.compose(deepcopy(parent1.genome), deepcopy(parent2.genome))
node1_fanin = new_genome.in_edges(node1, data='_type', keys=True)
node2_fanin = new_genome.in_edges(node2, data='_type', keys=True)
node1_fanin = new_genome.in_edges(node1, data=True, keys=True)
node2_fanin = new_genome.in_edges(node2, data=True, keys=True)
try:
node1_parent_link = rrandom.choice([(u, v, k, t) for u, v, k, t in node1_fanin if t == link_type])
node2_parent_link = rrandom.choice([(u, v, k, t) for u, v, k, t in node2_fanin if t == node1_parent_link[3]])
node1_parent_link = rrandom.choice([(u, v, k, d) for u, v, k, d in node1_fanin if d['_type'] == link_type])
node2_parent_link = rrandom.choice(
[(u, v, k, d) for u, v, k, d in node2_fanin if d['_type'] == node1_parent_link[3]['_type']]
)
except:
raise ByronOperatorFailure

new_genome.remove_edge(node1_parent_link[0], node1_parent_link[1], node1_parent_link[2])
new_genome.remove_edge(node2_parent_link[0], node2_parent_link[1], node2_parent_link[2])
new_genome.add_edge(node1_parent_link[0], node2_parent_link[1], node1_parent_link[2], _type=node1_parent_link[3])
new_genome.add_edge(node2_parent_link[0], node1_parent_link[1], node2_parent_link[2], _type=node1_parent_link[3])
# NOTE[GX]: replace link in node1_parent -> node1 with node1_parent -> node2 preserving links order
parent1_complete_fanout = list(new_genome.edges(node1_parent_link[0], data=True, keys=True))
for edge in parent1_complete_fanout:
new_genome.remove_edge(edge[0], edge[1], key=edge[2])
for edge in parent1_complete_fanout:
if edge == node1_parent_link:
new_genome.add_edge(
node1_parent_link[0], node2_parent_link[1], node1_parent_link[2], **node1_parent_link[3]
)
else:
new_genome.add_edge(edge[0], edge[1], key=edge[2], **edge[3])
# NOTE[GX]: replace link in node2_parent -> node2 with node2_parent -> node1 preserving links order
parent2_complete_fanout = list(new_genome.edges(node1_parent_link[0], data=True, keys=True))
for edge in parent2_complete_fanout:
new_genome.remove_edge(edge[0], edge[1], key=edge[2])
for edge in parent2_complete_fanout:
if edge == node2_parent_link:
new_genome.add_edge(
node2_parent_link[0], node1_parent_link[1], node2_parent_link[2], **node2_parent_link[3]
)
else:
new_genome.add_edge(edge[0], edge[1], key=edge[2], **edge[3])

discard_useless_components(new_genome)

if not get_structure_tree(new_genome):
raise ByronOperatorFailure

Node.reset_labels(new_genome)
new_individual = Individual(parent1.top_frame, new_genome)
assert new_individual.run_paranoia_checks()
# assert new_individual.run_paranoia_checks()

assert parent1.run_paranoia_checks()
assert parent2.run_paranoia_checks()
# assert parent1.run_paranoia_checks()
# assert parent2.run_paranoia_checks()
return [new_individual]


@genetic_operator(num_parents=2)
# @genetic_operator(num_parents=2)
def linked_node_crossover(parent1: Individual, parent2: Individual):
return _generic_node_crossover(parent1, parent2, LINK)
return _generic_node_crossover(parent1, parent2, only_targets=True, link_type=LINK)


@genetic_operator(num_parents=2)
def leaf_crossover_unfussy(parent1: Individual, parent2: Individual):
return _generic_node_crossover(parent1, parent2, choosy=False, only_targets=False, link_type=FRAMEWORK)


@genetic_operator(num_parents=2)
def leaf_crossover(parent1: Individual, parent2: Individual):
return _generic_node_crossover(parent1, parent2, FRAMEWORK)
def leaf_crossover_choosy(parent1: Individual, parent2: Individual):
return _generic_node_crossover(parent1, parent2, choosy=True, only_targets=False, link_type=FRAMEWORK)
1 change: 1 addition & 0 deletions byron/tools/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import networkx as nx

from byron.global_symbols import *
from byron.user_messages import *
from byron.classes.node_reference import NodeReference
from byron.classes.parameter import ParameterABC, ParameterStructuralABC
from byron.classes.selement import *
Expand Down
11 changes: 7 additions & 4 deletions examples/onemax/onemax-go/golang.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def framework():
int64 = byron.f.integer_parameter(0, 2**64)
math_op = byron.f.choice_parameter(['+', '-', '*', '/', '&', '^', '|'])
variable = byron.f.macro('var {_node} uint64', _label='')
variable.force_parent('prologue')
# variable.force_parent('prologue')

# standard
main_prologue = byron.f.macro('package main\nfunc evolved_function() uint64 {{')
Expand All @@ -50,7 +50,7 @@ def framework():
subs_prologue = byron.f.macro('func {_node}(foo uint64) uint64 {{', _label='')
subs_math = byron.f.macro('foo {op}= {num}', op=math_op, num=int64)
subs_epilogue = byron.f.macro('return foo\n}}')
subs = byron.f.sequence([subs_prologue, byron.f.bunch([subs_math], size=2), subs_epilogue], name='function')
subs = byron.f.sequence([subs_prologue, byron.f.bunch([subs_math], size=(2, 10)), subs_epilogue], name='function')

call = byron.f.macro(
'{var1} = {sub}({var2})',
Expand All @@ -59,5 +59,8 @@ def framework():
var2=byron.f.global_reference(variable),
)

code = byron.f.bunch([imath, vmath, call], size=5, name='body')
return byron.f.sequence((main_prologue, code, main_epilogue), max_instances=1)
few_default_vars = byron.f.bunch([variable], size=2)
# NOTE[GX]: Force all newly created variables to appear after the few default ones
variable.force_parent(few_default_vars)
code = byron.f.bunch([imath, vmath, call], weights=(3, 3, 1), size=(2, 20), name='body')
return byron.f.sequence((main_prologue, few_default_vars, code, main_epilogue), max_instances=1)
12 changes: 6 additions & 6 deletions examples/onemax/onemax-go/onemax-go.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@

@byron.fitness_function
def dummy_fitness(text):
return len(text)
return 1 / len(text)


def main():
top_frame = golang.framework()

# evaluator = byron.evaluator.ScriptEvaluator('./evaluate-all.sh', filename_format="individual{i:06}.go")
evaluator = byron.evaluator.ParallelScriptEvaluator(
'go', 'onemax.go', other_required_files=('main.go',), flags=('run',), timeout=10, default_result='-1'
)
# evaluator = byron.evaluator.PythonEvaluator(dummy_fitness)
evaluator = byron.evaluator.ScriptEvaluator('./evaluate-all.sh', filename_format="individual{i:06}.go")
# evaluator = byron.evaluator.ParallelScriptEvaluator(
# 'go', 'onemax.go', other_required_files=('main.go',), flags=('run',), timeout=10, default_result='-1'
# )

byron.f.set_option('$dump_node_info', True)
final_population = byron.ea.vanilla_ea(
top_frame,
evaluator,
max_generation=100,
max_generation=1_000,
mu=50,
lambda_=20,
max_fitness=64.0,
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

[tool.poetry]
name = "byron"
version = "0.8a1.dev15"
version = "0.8a1.dev16"
description = "Multi-purpose extensible self-adaptive optimizer and fuzzer"
authors = [
"Giovanni Squillero <giovanni.squillero@polito.it>",
Expand Down Expand Up @@ -96,7 +96,7 @@ max-line-length = 120
source-roots = ['src']

[bumpver]
current_version = "0.8a1.dev15"
current_version = "0.8a1.dev16"
version_pattern = "MAJOR.MINOR[PYTAGNUM].devINC0"
commit_message = "Bump version to {new_version}"
commit = false
Expand Down

0 comments on commit e838c60

Please sign in to comment.