예제 #1
0
class Analyzer():
    visited_nodes = set()
    core_labels = list()
    core_brackets = list()

    def __init__(self, nodes, edges):
        self.nodes = nodes
        self.edges = edges
        self.count = 0


    def dfs(self, node):
        self.visited_nodes.clear()
        self.doDfs(node)

    def doDfs(self, node):
        self.visited_nodes.add(node)
        assert(len(node.outputs) == 1 or len(node.outputs) == 0)
        # print("node", str(node.content.edit.text()), "outputs", len(node.outputs))
        if len(node.outputs) == 1:
            for edge in node.outputs[0].edges:
                toNode = edge.end_socket.node
                if toNode not in self.visited_nodes:
                    self.doDfs(toNode)

    def dfsCycle(self, node):
        self.colors = {}
        self.cycles = OrderedDict()
        self.cycles_edges = OrderedDict()
        self.parents = {}
        self.doDfsCycle(node)

    # def getCycle(self, start, end):
    #     cycle = []
    #     node = end
    #
    #     print(f"start {start.content.edit.text()}, end {end.content.edit.text()}")
    #     for child in self.parents:
    #         parents = self.parents[child]
    #         print(f"child {child.content.edit.text()} <- ", end="")
    #         print(",".join([parent.content.edit.text() for parent in parents]))
    #     while node != start:
    #         print(f"curr node {node.content.edit.text()}")
    #         cycle.append(node)
    #         node = self.parents[node].pop()
    #     cycle.append(node)
    #     self.parents[node].pop()
    #     cycle.reverse()
    #     return cycle
    #
    # def doDfsCycle(self, node):
    #     print("goto", node.content.edit.text())
    #     assert (len(node.outputs) == 1 or len(node.outputs) == 0)
    #     self.colors[node.id] = 1
    #     if len(node.outputs) == 1:
    #         for edge in node.outputs[0].edges:
    #             toNode = edge.end_socket.node
    #             self.parents.setdefault(toNode, list()).append(node)
    #             if self.colors.setdefault(toNode.id, 0) == 0:
    #                 self.doDfsCycle(toNode)
    #             elif self.colors[toNode.id] == 1:
    #                 cycle = self.getCycle(toNode, node)
    #                 self.cycles.setdefault(toNode, list()).append(cycle)
    #             print(f"return to {node.content.edit.text()}")
    #     self.colors[node.id] = 0

    def getCycle(self, start, path, edgePath):
        cycle = [start]
        cycleEdge = []
        idx = len(edgePath) - 1
        # print(f"start {start.content.edit.text()}, end {end.content.edit.text()}")
        # print(",".join([node.content.edit.text() for node in path]))
        for node in path[::-1]:
            cycle.append(node)
            cycleEdge.append(edgePath[idx])
            idx -= 1
            if node == start:
                break

        # assert(idx == -1)

        cycle.reverse()
        cycleEdge.reverse()
        return cycle, cycleEdge

    def doDfsCycle(self, node, path=list(), edgePath=list()):
        # print("goto", node.content.edit.text(), "path", ",".join([node.content.edit.text() for node in path]))
        assert (len(node.outputs) == 1 or len(node.outputs) == 0)
        self.colors[node.id] = 1
        pathCopy = path.copy()
        pathCopy.append(node)
        # print("pathCopy", ",".join([node.content.edit.text() for node in pathCopy]))
        if len(node.outputs) == 1:
            for edge in node.outputs[0].edges:
                toNode = edge.end_socket.node
                edgePathCopy = edgePath.copy()
                edgePathCopy.append(edge)
                self.parents.setdefault(toNode, list()).append(node)
                if self.colors.setdefault(toNode.id, 0) == 0:
                    self.doDfsCycle(toNode, pathCopy, edgePathCopy)
                elif self.colors[toNode.id] == 1:
                    cycle, cycleEdges = self.getCycle(toNode, pathCopy, edgePathCopy)
                    self.cycles.setdefault(toNode, list()).append(cycle)
                    self.cycles_edges.setdefault(toNode, list()).append(cycleEdges)
                # print(f"return to {node.content.edit.text()}", "path", ",".join([node.content.edit.text() for node in path]))
        self.colors[node.id] = 0


    def checkCorrectness(self):
        start_nodes = [node for node in self.nodes if isinstance(node, CalcNode_Input)]
        if len(start_nodes) != 1:
            return False, f"Начальных вершин {len(start_nodes)}"

        self.dfs(start_nodes[0])
        if len(self.visited_nodes) != len(self.nodes):
            return False, f"Граф несвязный"

        # if there is not way to output vertex from vertex then graph is incorrect
        for node in self.nodes:
            if not isinstance(node, CalcNode_Output) and not isinstance(node, CalcNode_Input):
                self.dfs(node)
                outputReachability = False
                for visited_node in self.visited_nodes:
                    if isinstance(visited_node, CalcNode_Output):
                        outputReachability = True
                        break
                if not outputReachability:
                    return False, f"Из вершины {str(node.content.edit.text())} нет пути до конца"

        return True, None

    def is_correct_brackets(brackets_trace):
        brackets = brackets_trace
        while '()' in brackets:
            brackets = brackets.replace('()', '')
        return not brackets

    def proceedCycles(self, cycles, edges_cycles):
        cycleCollectedStrings = (list(), list())

        for outer_idx in range(len(edges_cycles)):
            node_cycle = cycles[outer_idx]
            edge_cycle = edges_cycles[outer_idx]

            inner_idx = 0
            for edge in edge_cycle:
                nodeFrom, nodeTo = edge.start_socket.node, edge.end_socket.node
                assert(nodeFrom == node_cycle[inner_idx])
                assert(nodeTo == node_cycle[inner_idx + 1])
#                nodeTo = edge.end_soocket.node

                self.visited_nodes.add(nodeTo)
                cycleCollectedStrings[0].append(edge.edge_bracket)
                cycleCollectedStrings[1].append(edge.edge_label)

                inner_idx += 1

            #print("inner_idx", inner_idx, len(node_cycle))
            assert(inner_idx == len(node_cycle) - 1)

        # for idx, cycle in enumerate(cycles):
        #     if len(cycle) == 2:
        #         assert(cycle[0] == cycle[1])
        #         node = cycle[0]
        #         print(len(node.outputs[0].edges))

                # for edge in node.outputs[0].edges:
                #     if node == edge.end_socket.node and edge not in a:
                #         cycleCollectedStrings[0].append(edge.edge_bracket)
                #         cycleCollectedStrings[1].append(edge.edge_label)
                #         print("adding an edge to set:", edge.edge_label)
                #         a.add(edge)
                #     else:
                #         print("ebola")
                # continue
            # edgePath = edges_cycles
            # for edge in edgePath:
            #     self.visited_nodes.add(edge.end_socket.node)
            #     cycleCollectedStrings[0].append(edge.edge_bracket)
            #     cycleCollectedStrings[1].append(edge.edge_label)

            # for nodeFrom, nodeTo in zip(cycle[:-1], cycle[1:]):
            #     # print(f"from-to1 {nodeFrom.content.edit.text()}-{nodeTo.content.edit.text()}")
            #     currentEdge = None
            #     for edge in nodeFrom.outputs[0].edges:
            #         # print(f"edge in cycle {edge.end_socket.node.content.edit.text()}, label {edge.edge_label}, bool {nodeTo == edge.end_socket.node}")
            #         if nodeTo == edge.end_socket.node:
            #             currentEdge = edge
            #     assert currentEdge is not None
            #
            #     self.visited_nodes.add(nodeTo)
            #     cycleCollectedStrings[0].append(currentEdge.edge_bracket)
            #     cycleCollectedStrings[1].append(currentEdge.edge_label)
            #     # print(f"from-to2 {nodeFrom.content.edit.text()}-{nodeTo.content.edit.text()} label {currentEdge.edge_label}")


            # TODO: maybe bug
            for remainingCycle in cycles[outer_idx + 1:]:
                for node in remainingCycle:
                    self.visited_nodes.discard(node)

        for cycle in cycles:
            for node in cycle:
                self.visited_nodes.discard(node)

        return cycleCollectedStrings

    def checkOutputCorrectness(self, strings):
        brackets = strings[0]
        str_brackets = ''.join(brackets)
        while '()' in str_brackets:
            str_brackets = str_brackets.replace('()', '')
        #print(strings[1])
        str_lab = '  ->  '.join([(jo if jo != '' else '_') for jo in self.collectedStrings[1]])
        str_bra = '  ->  '.join([(jo if jo != '' else '_') for jo in self.collectedStrings[0]])
        if str_brackets == '':
            self.core_labels.append(str_lab)
            # self.core_labels.append([strings[1]])
            # print(self.core_labels)
            self.core_brackets.append(str_bra)
            self.count +=1
            # print(self.core_brackets)


    def dfsSubset(self, node):
        # print(f"goto {node.content.edit.text()}")
        self.visited_nodes.add(node)
        assert (len(node.outputs) == 1 or len(node.outputs) == 0)
        # print("node", str(node.content.edit.text()), "outputs", len(node.outputs))
        if len(node.outputs) == 0:  # output node
            #print("Got this:", self.collectedStrings)
            self.checkOutputCorrectness(self.collectedStrings)
            # self.core_labels.append()
        else:
            if node in self.cycles:
                # print(self.cycleNumbersMap)
                # for cycle in self.cycles[node]:
                #     print(f"Цикл от {node.content.edit.text()} содержит вершины: " + ' -> '.join(
                #         [node.content.edit.text() for node in cycle]))
                # print("subset: ", self.subset)
                processableCycles = list()
                processableEdges = list()

                # processableIdxes = list()

                # NB: keep this
                for idx, cycle in self.cycleNumbersMap[node]:
                    if idx in self.subset:
                        # processableIdxes.append(idx)
                        processableCycles.append(cycle)
                        processableEdge = None
                        for edgeIdx, edge in self.cycleEdgesNumbersMap[node]:
                            if edgeIdx == idx:
                                processableEdge = edge
                        assert(processableEdge is not None)
                        processableEdges.append(processableEdge)

                for permutation in permutations(range(len(processableCycles)), len(processableCycles)):
                    cyclesPermutation = []
                    edgeCyclesPermutation = []
                    # print("cycles len ", len(self.cycles), len(self.cycles_edges))
                    for idx in permutation:
                        cyclesPermutation.append(processableCycles[idx])
                        edgeCyclesPermutation.append(processableEdges[idx])

                    cycleCollectedStrings = self.proceedCycles(cyclesPermutation, edgeCyclesPermutation)
                    self.collectedStrings = (
                        self.collectedStrings[0] + cycleCollectedStrings[0],
                        self.collectedStrings[1] + cycleCollectedStrings[1]
                    )
                    self.visited_nodes.add(node)

                    # print("go to deep dfs cnt", cnt)
                    for edge in node.outputs[0].edges:
                        toNode = edge.end_socket.node
                        # print(f" if from {node.content.edit.text()} try {toNode.content.edit.text()}")
                        # print("visted:", ", ".join([node.content.edit.text() for node in self.visited_nodes]))
                        self.collectedStrings[0].append(edge.edge_bracket)
                        self.collectedStrings[1].append(edge.edge_label)
                        if toNode not in self.visited_nodes:
                            self.dfsSubset(toNode)
                            # print(f"return to {node.content.edit.text()}")
                        for item in self.collectedStrings:
                            item.pop()
                    # print("return from deep dfs cnt", cnt)
                    self.collectedStrings = (
                        self.collectedStrings[0][: len(self.collectedStrings[0]) - len(cycleCollectedStrings[0])],
                        self.collectedStrings[1][: len(self.collectedStrings[1]) - len(cycleCollectedStrings[1])]
                    )

                # for permutation in permutations(self.subset, len(self.subset)):
                #     # print(permutation)
                #     # print(f"cycle: {self.cycleNumbersMap[node]}, edges: {self.cycleEdgesNumbersMap[node]}")
                #
                #     for idx, cycle in self.cycleNumbersMap[node]:
                #         # print(idx, "check in: ", idx in permutation)
                #         if idx in permutation:
                #             processableCycles.append(cycle)
                #     # print(f"subset: {self.subset}")
                #     for idx_edge, cycle_edge in self.cycleEdgesNumbersMap[node]:
                #         if idx_edge in permutation:
                #             processableEdges.append(cycle_edge)



                # print("proc cycles: ------", processableCycles)
                # print("proc edges: -------", processableEdges)
                    # print("dghvvhjlhcgfxkjzfxcjgkcghvkcghkfxjg")
                #     perm = permutations(self.subset, len(self.subset))
                #     print(list(perm))

                # for permutation in permutations(processableCycles, len(processableCycles)):
                    # print("I'm in permutation cycle, wtf")
                    # print(f"Processable cycles permutation {len(permutation)}:")
                    # for cycle in permutation:

                    #     print(f"Цикл из перестановки от {node.content.edit.text()} содержит вершины: " + ' -> '.join(
                    #         [node.content.edit.text() for node in cycle]))
                # commented lower
                # cycleCollectedStrings = self.proceedCycles(processableCycles, processableEdges)
                # self.collectedStrings = (
                #     self.collectedStrings[0] + cycleCollectedStrings[0],
                #     self.collectedStrings[1] + cycleCollectedStrings[1]
                # )
                # self.visited_nodes.add(node)
                #
                # # print("go to deep dfs cnt", cnt)
                # for edge in node.outputs[0].edges:
                #     toNode = edge.end_socket.node
                #     # print(f" if from {node.content.edit.text()} try {toNode.content.edit.text()}")
                #     # print("visted:", ", ".join([node.content.edit.text() for node in self.visited_nodes]))
                #     self.collectedStrings[0].append(edge.edge_bracket)
                #     self.collectedStrings[1].append(edge.edge_label)
                #     if toNode not in self.visited_nodes:
                #         self.dfsSubset(toNode)
                #         # print(f"return to {node.content.edit.text()}")
                #     for item in self.collectedStrings:
                #         item.pop()
                # # print("return from deep dfs cnt", cnt)
                # self.collectedStrings = (
                # self.collectedStrings[0][: len(self.collectedStrings[0]) - len(cycleCollectedStrings[0])],
                # self.collectedStrings[1][: len(self.collectedStrings[1]) - len(cycleCollectedStrings[1])]
                # )
            else:
                for edge in node.outputs[0].edges:
                    toNode = edge.end_socket.node
                    # print(f" else from {node.content.edit.text()} try {toNode.content.edit.text()}")
                    # print("visted:", ", ".join([node.content.edit.text() for node in self.visited_nodes]))
                    self.collectedStrings[0].append(edge.edge_bracket)
                    self.collectedStrings[1].append(edge.edge_label)
                    if toNode not in self.visited_nodes:
                        self.dfsSubset(toNode)
                    for item in self.collectedStrings:
                        item.pop()
        self.visited_nodes.remove(node)

    def processSubset(self):
        self.visited_nodes.clear()
        self.collectedStrings = (list(), list())
        self.dfsSubset(self.start_node)

    def generateSubset(self, k):
        if k == self.cyclesNumber:
            self.processSubset()
        else:
            self.generateSubset(k + 1)
            self.subset.add(k)
            self.generateSubset(k + 1)
            self.subset.remove(k)

    def buildCore(self):
        self.cyclesNumber = 0
        self.subset = set()
        self.cycleNumbersMap = {}
        self.cycleEdgesNumbersMap = {}

        cyclesNumber = 0
        cyclesNumber_Edges = 0
        for node in self.cycles:
            self.cyclesNumber += len(self.cycles[node])

            self.cycleNumbersMap[node] = list()
            self.cycleEdgesNumbersMap[node] = list()
            for idx, cycle in enumerate(self.cycles[node]):
                self.cycleNumbersMap[node].append((cyclesNumber, cycle))
                cyclesNumber += 1
            for idx, edges_cycle in enumerate(self.cycles_edges[node]):
                self.cycleEdgesNumbersMap[node].append((cyclesNumber_Edges, edges_cycle))
                cyclesNumber_Edges += 1

        print("cycles number =", self.cyclesNumber)
        #print(self.cycleNumbersMap)
        #print(self.cycleEdgesNumbersMap)

        self.generateSubset(0)
        #print(f'Labels: {self.core_labels}')
        #print(f'Brackets: {self.core_brackets}')
        msg = ''
        #print(self.count)
        for idx in range(self.count):
            msg += (f'Пометки:   {self.core_labels[idx]} \nСкобки   :   {self.core_brackets[idx]}')
            msg += '\n\n'

        return self.core_labels, self.core_brackets, msg[:-1]

    def checkCycles(self, node = 0, wd = 0):
        fin_str = ''
        for node in self.nodes:
            if isinstance(node, CalcNode_Input):
                self.start_node = node
        self.dfsCycle(self.start_node)
        for node in self.cycles:
            for cycle in self.cycles[node]:
                str = f"Цикл от {node.content.edit.text()} содержит дуги: " + ' -> '.join([node.content.edit.text() for node in cycle])
                print(str)
                fin_str += str + ';\n'
        return fin_str[:-2]+'.'