Skip to content

Commit

Permalink
feat: add providers' idea
Browse files Browse the repository at this point in the history
clean up get_nodes
  • Loading branch information
squillero committed Aug 13, 2024
1 parent 0157d7a commit 729f737
Show file tree
Hide file tree
Showing 22 changed files with 71,060 additions and 561 deletions.
30 changes: 16 additions & 14 deletions byron/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from byron.registry import *
from byron.sys import SYSINFO as sysinfo
from byron.tools.entropy import *
from byron.tools.providers import *
from byron.tools.graph import fasten_subtree_parameters
from byron.user_messages.messaging import logger

Expand All @@ -55,9 +56,10 @@
# f'This is Byron v{__version__} "{__codename__}"\n' + f"(c) 2023 G. Squillero & A. Tonda β€” Licensed under Apache-2.0"
# )


__welcome__ = (
f'[bold]This is Byron v{__version__.rsplit(".", maxsplit=1)[0]} "[italic]{__codename__}[/italic]"[/]\n'
+ "[bold](c) 2023-24 G. Squillero & A. Tonda β€” Licensed under Apache-2.0[/]"
f'[bold]This is Byron v{__version__.rsplit(".", maxsplit=1)[0]} "[italic]{__codename__}[/italic]"[/]\n'
+ "[bold](c) 2023-24 G. Squillero & A. Tonda β€” Licensed under Apache-2.0[/]"
)


Expand Down Expand Up @@ -93,21 +95,21 @@ def welcome(level=logging.DEBUG):

if notebook_mode and logging.getLogger().level <= logging.WARNING and paranoia_mode:
assert (
test_mode
or not main_process
or user_messages.performance_warning(
"Paranoia checks are enabled in this notebook: performances can be significantly impaired\n"
+ "[see https://cad-polito-it.github.io/byron/paranoia for details]"
)
test_mode
or not main_process
or user_messages.performance_warning(
"Paranoia checks are enabled in this notebook: performances can be significantly impaired\n"
+ "[see https://cad-polito-it.github.io/byron/paranoia for details]"
)
)
elif not notebook_mode:
assert (
test_mode
or not main_process
or user_messages.performance_warning(
"Paranoia checks are enabled: performances can be significantly impaired β€” consider using '-O'\n"
+ "[see https://cad-polito-it.github.io/byron/paranoia for details]"
)
test_mode
or not main_process
or user_messages.performance_warning(
"Paranoia checks are enabled: performances can be significantly impaired β€” consider using '-O'\n"
+ "[see https://cad-polito-it.github.io/byron/paranoia for details]"
)
)

if not matplotlib_available:
Expand Down
18 changes: 9 additions & 9 deletions byron/classes/_individual_as.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ def _draw_forest(self, zoom):
for n in T:
T.nodes[n]["depth"] = len(nx.shortest_path(T, 0, n))
height = max(T.nodes[n]["depth"] for n in T.nodes)
width = sum(1 for n in T if self.G.nodes[n]['_type'] == MACRO_NODE)
width = sum(1 for n in T if self.G.nodes[n]['_type'] == MACRO)
fig = plt.figure(figsize=(10 + zoom * width * 0.8, zoom * height + width / 2))
ax = fig.add_subplot()

Expand All @@ -162,12 +162,12 @@ def _draw_forest(self, zoom):
T, pos, style=":", edge_color="lightgray", arrowstyle="-|>,head_length=.6,head_width=0.2", ax=ax
)
# draw macros
nodelist = [n for n in T if self.G.nodes[n]['_type'] == MACRO_NODE]
nodelist = [n for n in T if self.G.nodes[n]['_type'] == MACRO]
nx.draw_networkx_nodes(
T, pos, nodelist=nodelist, node_color=[colors[n] for n in nodelist], node_size=800, cmap=plt.cm.tab20, ax=ax
)
# draw frames
nodelist = [n for n in self.G if self.G.nodes[n]['_type'] == FRAME_NODE]
nodelist = [n for n in self.G if self.G.nodes[n]['_type'] == FRAME]
nx.draw_networkx_nodes(
T,
pos,
Expand Down Expand Up @@ -223,7 +223,7 @@ def _draw_multipartite(self, zoom: int) -> None:
T = self.structure_tree.copy()
extra_heads = list()
for node in list(v for _, v in T.edges(NODE_ZERO) if self.genome.nodes[v]['_selement'].FORCED_PARENT):
if self.genome.nodes[node]['_type'] == MACRO_NODE:
if self.genome.nodes[node]['_type'] == MACRO:
extra_heads.append(node)
target = self.genome.nodes[node]['_selement'].FORCED_PARENT
T.remove_edge(NODE_ZERO, node)
Expand All @@ -233,7 +233,7 @@ def _draw_multipartite(self, zoom: int) -> None:
G = nx.DiGraph()
sub_graphs = list()
for s, head in enumerate(n for _, n in T.edges(0)):
nodes = [n for n in nx.dfs_preorder_nodes(T, head) if self.G.nodes[n]['_type'] == MACRO_NODE]
nodes = [n for n in nx.dfs_preorder_nodes(T, head) if self.G.nodes[n]['_type'] == MACRO]
sub_graphs.append(nodes)
G.add_nodes_from(nodes)
for n1, n2 in zip(nodes, nodes[1:]):
Expand Down Expand Up @@ -271,7 +271,7 @@ def _draw_multipartite(self, zoom: int) -> None:
)

labels = dict()
for n in [_ for _ in pos if self.genome.nodes[n]['_type'] == MACRO_NODE]:
for n in [_ for _ in pos if self.genome.nodes[n]['_type'] == MACRO]:
pos[n] = (pos[n][0], pos[n][1])
d = NodeView(NodeReference(self.genome, n)).safe_dump
labels[n] = ' ' + d.split('\n')[0].strip() + (' …' if '\n' in d else '')
Expand All @@ -283,7 +283,7 @@ def _draw_multipartite(self, zoom: int) -> None:
G.add_edges_from(
(u, v)
for u, v, k in self.G.edges(data='_type')
if k == LINK and self.G.nodes[u]['_type'] == MACRO_NODE and u in s and v in s
if k == LINK and self.G.nodes[u]['_type'] == MACRO and u in s and v in s
)
nx.draw_networkx_edges(
G,
Expand Down Expand Up @@ -321,8 +321,8 @@ def _draw_multipartite(self, zoom: int) -> None:


def patch_pos(
G: nx.DiGraph,
pos: dict,
G: nx.DiGraph,
pos: dict,
):
pos_order = [n for n in list(nx.dfs_preorder_nodes(G, NODE_ZERO)) if n in pos]
swap_x, swap_y = 0, 0
Expand Down
117 changes: 55 additions & 62 deletions byron/classes/individual.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,6 @@
from byron.global_symbols import *
from byron.tools.graph import *
from byron.user_messages import *

if matplotlib_available:
pass

from byron.classes.byron import Byron
from byron.classes.dump import *
from byron.classes.fitness import FitnessABC
Expand All @@ -56,7 +52,6 @@
from byron.classes.paranoid import Paranoid
from byron.classes.readymade_macros import MacroZero
from byron.classes.value_bag import ValueBag
from byron.global_symbols import *


@dataclass(frozen=True, slots=True)
Expand Down Expand Up @@ -127,7 +122,8 @@ def __init__(self, top_frame: type[FrameABC], genome: nx.MultiDiGraph | None = N
self._genome = genome
else:
self._genome = nx.MultiDiGraph(top_frame=top_frame)
self._genome.add_node(NODE_ZERO, _selement=MacroZero(), _type=MACRO_NODE)
self._genome.add_node(NODE_ZERO, _selement=MacroZero(), _type=MACRO)
self._genome.graph[ATTRIBUTE_PROVIDERS] = set()
self._fitness = None
self._str = ''
self._lineage = None
Expand All @@ -142,11 +138,11 @@ def __str__(self):

def __eq__(self, other) -> bool:
return (
type(self) == type(other)
and self._fitness == other._fitness
and nx.isomorphism.is_isomorphic(
self._genome, other._genome, node_match=operator.eq, edge_match=operator.eq
)
type(self) == type(other)
and self._fitness == other._fitness
and nx.isomorphism.is_isomorphic(
self._genome, other._genome, node_match=operator.eq, edge_match=operator.eq
)
)

def __hash__(self) -> int:
Expand Down Expand Up @@ -257,11 +253,11 @@ def fitness(self, value: FitnessABC) -> None:
assert self._check_fitness(value)
self._fitness = value
if any(value >> i.fitness for i in self._lineage.parents) and any(
value >> i.fitness or not value.is_distinguishable(i.fitness) for i in self.lineage.parents
value >> i.fitness or not value.is_distinguishable(i.fitness) for i in self.lineage.parents
):
self._lineage.operator.stats.successes += 1
elif any(value << i.fitness for i in self.lineage.parents) and any(
value << i.fitness or not value.is_distinguishable(i.fitness) for i in self.lineage.parents
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}/{self.lineage} is {value}")
Expand All @@ -279,29 +275,29 @@ def as_message(self) -> list[int]:
# CACHED PROPERTIED

@property
def macros(self) -> tuple[Macro]:
def macros(self) -> list[Macro]:
"""Return all macro instances in unreliable order."""
return tuple(
self._genome.nodes[n]['_selement'] for n in self._genome if self._genome.nodes[n]['_type'] == MACRO_NODE
return list(
self._genome.nodes[n]['_selement'] for n in self._genome if self._genome.nodes[n]['_type'] == MACRO
)

@property
def frames(self) -> tuple[FrameABC]:
def frames(self) -> list[FrameABC]:
"""Return all frame instances in unreliable order."""
return tuple(
self._genome.nodes[n]['_selement'] for n in self._genome if self._genome.nodes[n]['_type'] == FRAME_NODE
return list(
self._genome.nodes[n]['_selement'] for n in self._genome if self._genome.nodes[n]['_type'] == FRAME
)

@property
def parameters(self) -> tuple[ParameterABC]:
def parameters(self) -> list[ParameterABC]:
"""Return all parameter instances in unreliable order."""
return tuple(p for n in self._genome for p in self._genome.nodes[n].values() if isinstance(p, ParameterABC))
return list(p for n in self._genome for p in self._genome.nodes[n].values() if isinstance(p, ParameterABC))

@property
def structure_tree(self) -> nx.classes.DiGraph:
"""A tree with the structure tree of the individual (ie. only edges of `kind=FRAMEWORK`)."""
tree = get_structure_tree(self._genome)
assert tree, f"{PARANOIA_VALUE_ERROR}: Structure of {self!r} is not a valid tree"
"""A tree with the structure of the individual (ie. only edges of `kind=FRAMEWORK`)."""
tree = get_structure(self._genome)
assert nx.is_arborescence(tree), f"{PARANOIA_VALUE_ERROR}: Structure tree is not a tree!?"
return tree

#######################################################################
Expand All @@ -328,21 +324,21 @@ def run_paranoia_checks(self) -> bool:
G.add_edges_from(self.G.edges)
G.remove_node(NODE_ZERO)
assert (
sum(1 for _ in nx.weakly_connected_components(G)) == 1
sum(1 for _ in nx.weakly_connected_components(G)) == 1
), f"{PARANOIA_TYPE_ERROR}: Individual is not a weakly connected graph"

assert nx.is_branching(self.structure_tree) and nx.is_weakly_connected(
self.structure_tree
), f"{PARANOIA_VALUE_ERROR}: Structure_tree of {self!r} is not a tree"

assert set(self.genome.nodes) == set(self.structure_tree.nodes), (
f"{PARANOIA_VALUE_ERROR}: Node mismatch with structure tree: "
+ f"{set(self.genome.nodes) ^ set(self.structure_tree.nodes)}"
f"{PARANOIA_VALUE_ERROR}: Node mismatch with structure tree: "
+ f"{set(self.genome.nodes) ^ set(self.structure_tree.nodes)}"
)

# ==[check genome (fitness)]=========================================
assert (self._fitness is None and not self.finalized) or (
self._fitness is not None and self.finalized
self._fitness is not None and self.finalized
), "Value Error (paranoia check): Mismatch fitness and is_finalized"

# ==[check edges (semantic)]=========================================
Expand All @@ -366,19 +362,19 @@ def run_paranoia_checks(self) -> bool:
assert isinstance(self._genome.nodes[0]['_selement'], MacroZero), f"{PARANOIA_TYPE_ERROR}: Incorrect NodeZero"

# ==[check structural parameter]=====================================
for node in (n for n, t in self._genome.nodes(data='_type') if t == MACRO_NODE):
for node in (n for n, t in self._genome.nodes(data='_type') if t == MACRO):
for p_name, p_type in (
(p, P)
for p, P in self._genome.nodes[node]['_selement'].parameter_types.items()
if issubclass(P, ParameterStructuralABC)
(p, P)
for p, P in self._genome.nodes[node]['_selement'].parameter_types.items()
if issubclass(P, ParameterStructuralABC)
):
assert (
sum(
1
for u, v, k in self._genome.out_edges(node, keys=True)
if k == self._genome.nodes[node][p_name].key
)
== 1
sum(
1
for u, v, k in self._genome.out_edges(node, keys=True)
if k == self._genome.nodes[node][p_name].key
)
== 1
), f"{PARANOIA_VALUE_ERROR}: Problem with parameter '{p_name}' {p_type}"
keys = [
self._genome.nodes[node][p].key
Expand All @@ -397,13 +393,13 @@ def run_paranoia_checks(self) -> bool:

structural_edges = [(u, v, k) for u, v, k, d in self._genome.edges(data='_type', keys=True) if d == LINK]
assert len(structural_edges) == len(set(k for u, v, k in structural_edges)), (
f"{PARANOIA_VALUE_ERROR}: Found duplicated keys in structural edges: "
+ f"{set(x for i, x in enumerate(list(k for u, v, k in structural_edges)) if i != list(k for u, v, k in structural_edges).index(x))}"
f"{PARANOIA_VALUE_ERROR}: Found duplicated keys in structural edges: "
+ f"{set(x for i, x in enumerate(list(k for u, v, k in structural_edges)) if i != list(k for u, v, k in structural_edges).index(x))}"
)
structural_parameters = [p for p in self.parameters if isinstance(p, ParameterStructuralABC)]
assert len(structural_edges) == len(structural_parameters), (
f"{PARANOIA_VALUE_ERROR}: Inconsistent number of structural edges: "
+ f"found {len(structural_edges)}, expecting {len(structural_parameters)}"
f"{PARANOIA_VALUE_ERROR}: Inconsistent number of structural edges: "
+ f"found {len(structural_edges)}, expecting {len(structural_parameters)}"
)
assert set(k for u, v, k in structural_edges) == set(
p._key for p in structural_parameters
Expand All @@ -412,14 +408,14 @@ def run_paranoia_checks(self) -> bool:
return True

def describe(
self,
*,
include_fitness: bool = True,
include_structure: bool = True,
include_age: bool = True,
include_lineage: bool = True,
max_recursion: int = 0,
_indent_level: str = '',
self,
*,
include_fitness: bool = True,
include_structure: bool = True,
include_age: bool = True,
include_lineage: bool = True,
max_recursion: int = 0,
_indent_level: str = '',
):
desc = str(self)
delem = list()
Expand All @@ -428,15 +424,15 @@ def describe(
if include_structure:
node_types = list(t for n, t in self.G.nodes(data='_type'))
n_nodes = len(self.G)
n_macros = node_types.count(MACRO_NODE) - 1
n_frames = node_types.count(FRAME_NODE)
n_macros = node_types.count(MACRO) - 1
n_frames = node_types.count(FRAME)
n_links = sum(True for _, _, k in self.G.edges(data='_type') if k != FRAMEWORK)
n_params = sum(
True
for p in chain.from_iterable(
self.G.nodes[n]['_selement'].parameter_types.items()
for n in self.G
if self.G.nodes[n]['_type'] == MACRO_NODE
if self.G.nodes[n]['_type'] == MACRO
)
)
delem.append(
Expand Down Expand Up @@ -480,10 +476,7 @@ def dump(self, extra_parameters: dict | None = None) -> str:
extra_parameters = DEFAULT_EXTRA_PARAMETERS | DEFAULT_OPTIONS

# =[Flatten the graph into a list of nodes]==========================
tree = make_digraph_cached(
tuple(self._genome.nodes), tuple((u, v) for u, v, k in self._genome.edges(data='_type') if k == FRAMEWORK)
)
tree = tree.copy()
tree = get_structure(self._genome).copy()

for node in list(v for _, v in tree.edges(NODE_ZERO) if self.genome.nodes[v]['_selement'].FORCED_PARENT):
target = self.genome.nodes[node]['_selement'].FORCED_PARENT
Expand All @@ -510,15 +503,15 @@ def dump(self, extra_parameters: dict | None = None) -> str:
node_str = '{_text_before_node}'.format(**bag)
if nr.graph.in_degree(nr.node) > 1:
node_str += bag['_label'].format(**bag)
if nr.graph.nodes[nr.node]['_type'] == MACRO_NODE:
if nr.graph.nodes[nr.node]['_type'] == MACRO:
node_str += '{_text_before_macro}'.format(**bag)
node_str += nr.graph.nodes[nr.node]['_selement'].dump(bag)
if bag['$dump_node_info'] and nr.node != NODE_ZERO:
if node_str:
node_str += ' '
node_str += '{_comment} πŸ–‹ {_node.path_string} ➜ {_node.type_}'.format(**bag)
node_str += '{_text_after_macro}'.format(**bag)
elif nr.graph.nodes[nr.node]['_type'] == FRAME_NODE:
elif nr.graph.nodes[nr.node]['_type'] == FRAME:
node_str += '{_text_before_frame}'.format(**bag)
if bag['$dump_node_info']:
node_str += '{_comment} πŸ–‹ {_node.path_string} ➜ {_node.type_}{_text_after_macro}'.format(**bag)
Expand All @@ -534,7 +527,7 @@ def dump(self, extra_parameters: dict | None = None) -> str:

@staticmethod
def _recursive_flatten_frames(
nr: NodeReference, T: nx.DiGraph, extra_parameters: dict, path: tuple, dump: list
nr: NodeReference, T: nx.DiGraph, extra_parameters: dict, path: tuple, dump: list
) -> list:
local_parameters = copy(extra_parameters)
local_parameters |= nr.graph.nodes[nr.node]['_selement'].EXTRA_PARAMETERS
Expand All @@ -558,13 +551,13 @@ def _dump_node_recursive(nr: NodeReference, T: nx.DiGraph, extra_parameters: dic
node_str = '{_text_before_node}'.format(**bag)
if nr.graph.in_degree(nr.node) > 1:
node_str += bag['_label'].format(**bag)
if nr.graph.nodes[nr.node]['_type'] == MACRO_NODE:
if nr.graph.nodes[nr.node]['_type'] == MACRO:
node_str += '{_text_before_macro}'.format(**bag)
node_str += nr.graph.nodes[nr.node]['_selement'].dump(bag)
if bag['$dump_node_info']:
node_str += ' {_comment} πŸ–‹ {_node.path_string} ➜ {_node.type_}'.format(**bag)
node_str += '{_text_after_macro}'.format(**bag)
elif nr.graph.nodes[nr.node]['_type'] == FRAME_NODE:
elif nr.graph.nodes[nr.node]['_type'] == FRAME:
node_str += '{_text_before_frame}'.format(**bag)
if bag['$dump_node_info']:
node_str += '{_comment} πŸ–‹ {_node.path_string} ➜ {_node.type_}{_text_after_macro}'.format(**bag)
Expand Down
Loading

0 comments on commit 729f737

Please sign in to comment.