<reponame>daviddoret/pyxag<filename>functions/substitute_nag_function.py
from functions.get_nag_vertex_type import get_nag_vertex_type
from functions.get_nag_vertex_number import get_nag_vertex_number
import logging


def substitute_nag_vertex(nag, old_vertex, new_vertex, remove_old_vertex=True):
    """Replaces all occurrences of old_vertex with new_vertex."""
    logging.debug('substitute_nag_vertex(nag, {0}, {1}, {2})'.format(old_vertex, new_vertex, remove_old_vertex))

    # Check the compatibility of the two vertexs
    if get_nag_vertex_type(old_vertex) != get_nag_vertex_type(new_vertex):
        raise ValueError('Incompatible vertexs', old_vertex, new_vertex)

    if get_nag_vertex_number(old_vertex) < get_nag_vertex_number(new_vertex):
        # Check that the substitution would not yield an illegal graph cycle (and fail).
        if nag.get_vertex_successors_min_number(old_vertex) is not None and \
                nag.get_vertex_successors_min_number(old_vertex) < get_nag_vertex_number(new_vertex):
            raise ValueError('Old vertex min successors number < new vertex number', old_vertex, new_vertex)

    if get_nag_vertex_number(old_vertex) > get_nag_vertex_number(new_vertex):
        # Check that the substitution would not yield an illegal graph cycle (and fail).
        if nag.get_vertex_predecessors_max_number(old_vertex) is not None and \
                nag.get_vertex_predecessors_max_number(old_vertex) > get_nag_vertex_number(new_vertex):
            raise ValueError('Old vertex max predecessors number > new vertex number', old_vertex, new_vertex)

    # It is now safe to substitute the old vertex with the new one.
    logging.debug('Substition is safe')

    # First, set the new vertex with the same predecessors than the old one.
    # This is only applicable to vertex types that have predecessors, i.e. NANDs and outputs.
    if get_nag_vertex_type(new_vertex) == 'n':
        predecessors = nag.get_predecessors_as_list(old_vertex)
        logging.debug('predecessors:{0}'.format(predecessors))
        predecessor_1 = predecessors[0]
        predecessor_2 = None
        if len(predecessors) == 1:
            # In a MultiDiGraph, a NAND vertex may have a single predecessor.
            # This happens when its two inputs are identical.
            predecessor_2 = predecessor_1
        elif len(predecessors) == 2:
            # Otherwise, when the NAND vertex has two distinct inputs,
            # it has two predecessors.
            predecessor_2 = predecessors[1]
        nag.set_nand(predecessor_1, predecessor_2, new_vertex)
    if get_nag_vertex_type(new_vertex) == 'o':
        predecessors = nag.get_predecessors_as_list(old_vertex)
        logging.debug('predecessors:{0}'.format(predecessors))
        predecessor_1 = predecessors[0]
        nag.set_output(predecessor_1, new_vertex)

    # Second, find all successors and overwrite them with the new vertex as predecessor.
    for successor in nag.get_successors_as_list(old_vertex):
        if get_nag_vertex_type(successor) == 'n':
            predecessors = nag.get_predecessors_as_list(successor)
            predecessor_1 = predecessors[0]
            predecessor_2 = None
            if len(predecessors) == 1:
                # In a MultiDiGraph, a NAND vertex may have a single predecessor.
                # This happens when its two inputs are identical.
                predecessor_2 = predecessor_1
            elif len(predecessors) == 2:
                # Otherwise, when the NAND vertex has two distinct inputs,
                # it has two predecessors.
                predecessor_2 = predecessors[1]
            if predecessor_1 == old_vertex:
                predecessor_1 = new_vertex
            if predecessor_2 == old_vertex:
                predecessor_2 = new_vertex
            nag.set_nand(predecessor_1, predecessor_2, successor)
        if get_nag_vertex_type(successor) == 'o':
            nag.set_output(new_vertex, successor)

    # Finally, delete the old vertex.
    # This automatically removes all remaining adjacent edges.
    # Source: https://networkx.github.io/documentation/latest/reference/classes/generated/networkx.Graph.remove_node.html
    if remove_old_vertex:
        nag.graph.remove_node(old_vertex)



from functions.get_nag_vertex_type import get_nag_vertex_type
from functions.get_nag_vertex_number import get_nag_vertex_number
import logging


def substitute_nag_vertex(nag, old_vertex, new_vertex, remove_old_vertex=True):
    """Replaces all occurrences of old_vertex with new_vertex."""
    logging.debug('substitute_nag_vertex(nag, {0}, {1}, {2})'.format(old_vertex, new_vertex, remove_old_vertex))

    # Check the compatibility of the two vertexs
    if get_nag_vertex_type(old_vertex) != get_nag_vertex_type(new_vertex):
        raise ValueError('Incompatible vertexs', old_vertex, new_vertex)

    if get_nag_vertex_number(old_vertex) < get_nag_vertex_number(new_vertex):
        # Check that the substitution would not yield an illegal graph cycle (and fail).
        if nag.get_vertex_successors_min_number(old_vertex) is not None and \
                nag.get_vertex_successors_min_number(old_vertex) < get_nag_vertex_number(new_vertex):
            raise ValueError('Old vertex min successors number < new vertex number', old_vertex, new_vertex)

    if get_nag_vertex_number(old_vertex) > get_nag_vertex_number(new_vertex):
        # Check that the substitution would not yield an illegal graph cycle (and fail).
        if nag.get_vertex_predecessors_max_number(old_vertex) is not None and \
                nag.get_vertex_predecessors_max_number(old_vertex) > get_nag_vertex_number(new_vertex):
            raise ValueError('Old vertex max predecessors number > new vertex number', old_vertex, new_vertex)

    # It is now safe to substitute the old vertex with the new one.
    logging.debug('Substition is safe')

    # First, set the new vertex with the same predecessors than the old one.
    # This is only applicable to vertex types that have predecessors, i.e. NANDs and outputs.
    if get_nag_vertex_type(new_vertex) == 'n':
        predecessors = nag.get_predecessors_as_list(old_vertex)
        logging.debug('predecessors:{0}'.format(predecessors))
        predecessor_1 = predecessors[0]
        predecessor_2 = None
        if len(predecessors) == 1:
            # In a MultiDiGraph, a NAND vertex may have a single predecessor.
            # This happens when its two inputs are identical.
            predecessor_2 = predecessor_1
        elif len(predecessors) == 2:
            # Otherwise, when the NAND vertex has two distinct inputs,
            # it has two predecessors.
            predecessor_2 = predecessors[1]
        nag.set_nand(predecessor_1, predecessor_2, new_vertex)
    if get_nag_vertex_type(new_vertex) == 'o':
        predecessors = nag.get_predecessors_as_list(old_vertex)
        logging.debug('predecessors:{0}'.format(predecessors))
        predecessor_1 = predecessors[0]
        nag.set_output(predecessor_1, new_vertex)

    # Second, find all successors and overwrite them with the new vertex as predecessor.
    for successor in nag.get_successors_as_list(old_vertex):
        if get_nag_vertex_type(successor) == 'n':
            predecessors = nag.get_predecessors_as_list(successor)
            predecessor_1 = predecessors[0]
            predecessor_2 = None
            if len(predecessors) == 1:
                # In a MultiDiGraph, a NAND vertex may have a single predecessor.
                # This happens when its two inputs are identical.
                predecessor_2 = predecessor_1
            elif len(predecessors) == 2:
                # Otherwise, when the NAND vertex has two distinct inputs,
                # it has two predecessors.
                predecessor_2 = predecessors[1]
            if predecessor_1 == old_vertex:
                predecessor_1 = new_vertex
            if predecessor_2 == old_vertex:
                predecessor_2 = new_vertex
            nag.set_nand(predecessor_1, predecessor_2, successor)
        if get_nag_vertex_type(successor) == 'o':
            nag.set_output(new_vertex, successor)

    # Finally, delete the old vertex.
    # This automatically removes all remaining adjacent edges.
    # Source: https://networkx.github.io/documentation/latest/reference/classes/generated/networkx.Graph.remove_node.html
    if remove_old_vertex:
        nag.graph.remove_node(old_vertex)
Replaces all occurrences of old_vertex with new_vertex. # Check the compatibility of the two vertexs # Check that the substitution would not yield an illegal graph cycle (and fail). # Check that the substitution would not yield an illegal graph cycle (and fail). # It is now safe to substitute the old vertex with the new one. # First, set the new vertex with the same predecessors than the old one. # This is only applicable to vertex types that have predecessors, i.e. NANDs and outputs. # In a MultiDiGraph, a NAND vertex may have a single predecessor. # This happens when its two inputs are identical. # Otherwise, when the NAND vertex has two distinct inputs, # it has two predecessors. # Second, find all successors and overwrite them with the new vertex as predecessor. # In a MultiDiGraph, a NAND vertex may have a single predecessor. # This happens when its two inputs are identical. # Otherwise, when the NAND vertex has two distinct inputs, # it has two predecessors. # Finally, delete the old vertex. # This automatically removes all remaining adjacent edges. # Source: https://networkx.github.io/documentation/latest/reference/classes/generated/networkx.Graph.remove_node.html