Пример #1
0
    def read_labeldict(self) -> list:
        """
        Read edges from '_td_bag' and the labels from 'td_node_status' for the bags.

        Returns
        -------
        list
            The filled labeldict for visualization.

        """
        with self.connection.cursor() as cur:  # create a cursor
            labeldict = []
            # check bag numbering:
            bags = sorted(
                list(flatten(query_td_bag_grouped(cur, self.problem))))
            LOGGER.debug("bags: %s", bags)
            for bag in bags:
                nodes = list(flatten(query_td_bag(cur, self.problem, bag)))
                start_time, dtime = query_td_node_status(
                    cur, self.problem, bag)
                labeldict.append({
                    'id':
                    bag,
                    'items':
                    nodes,
                    'labels': [
                        str(nodes),
                        "dtime=%.4fs" % dtime.total_seconds(),
                        # start_time.strftime("%D %T")
                    ]
                })
            return labeldict
Пример #2
0
    def prepare_incidence(self, incid, _timeline, view):
        """Prepare incidence construction."""
        if incid.infer_primal or incid.infer_dual:
            # prepare incid edges with abs:
            abs_clauses = [[cl[0], list(map(abs, cl[1]))]
                           for cl in incid.edges]
        if incid.infer_primal:
            # vertex for each variable + edge if the variables
            # occur in the same clause:
            primal_edges = set(
                flatten(  # remove duplicates
                    [itertools.combinations(cl[1], 2) for cl in abs_clauses]))
            # check if any node is really isolated:
            isolated = [
                cl[1][0] for cl in abs_clauses
                if len(cl[1]) == 1 and not any(cl[1][0] in sl
                                               for sl in primal_edges)
            ]

            self.general_graph(timeline=_timeline,
                               edges=primal_edges,
                               extra_nodes=set(isolated),
                               graph_name=incid.primal_file,
                               file_basename=incid.primal_file,
                               var_name=incid.var_name_two)
            LOGGER.info("Created infered primal-graph")

        if incid.infer_dual:
            # Edge, if clauses share the same variable
            dual_edges = [
                (cl[0], other[0]) for i, cl in enumerate(abs_clauses)
                for other in abs_clauses[i + 1:]  # no multiples
                if any(var in cl[1] for var in other[1])
            ]
            # check if any clause is isolated:
            isolated = [
                cl[0] for cl in abs_clauses
                if not any(cl[0] in sl for sl in dual_edges)
            ]

            self.general_graph(timeline=_timeline,
                               edges=dual_edges,
                               extra_nodes=set(isolated),
                               graph_name=incid.dual_file,
                               file_basename=incid.dual_file,
                               var_name=incid.var_name_one)
            LOGGER.info("Created infered dual-graph")

        self.incidence(edges=incid.edges,
                       timeline=_timeline,
                       inc_file=incid.inc_file,
                       num_vars=self.tree_dec['num_vars'],
                       colors=self.data.colors,
                       view=view,
                       fontsize=incid.fontsize,
                       penwidth=incid.penwidth,
                       basefill=self.data.bagcolor,
                       var_name_one=incid.var_name_one,
                       var_name_two=incid.var_name_two,
                       column_distance=incid.column_distance)
Пример #3
0
    def incidence(self,
                  timeline: Iterable[Optional[List[int]]],
                  num_vars: int,
                  colors: List,
                  edges: List,
                  inc_file: str = 'IncidenceGraphStep',
                  view: bool = False,
                  fontsize: Union[str, int] = 16,
                  penwidth: float = 2.2,
                  basefill: str = 'white',
                  sndshape: str = 'diamond',
                  neg_tail: str = 'odot',
                  var_name_one: str = '',
                  var_name_two: str = '',
                  column_distance: float = 0.5) -> None:
        """
        Creates the incidence graph emphasized for the given timeline.

        Parameters
        ----------
        timeline : Iterable of: None | [int...]
            None if no variables get highlighted in this step.
            Else the 'timeline' provides the set of variables that are
            in the bag(s) under consideration. This function computes all other
            variables that are involved in this timestep using the 'edgelist'.
        num_vars : int
            Count of variables that are used in the clauses.
        colors : Iterable of color
            Colors to use for the graph parts.
        inc_file : string
            Basis for the file created, gets appended with the step number.
        view : bool
            If true opens the created file after creation. Default is false.
        basefill : color
            Background color of all nodes. Default is 'white'.
        sndshape : string
            Description of the shape for nodes with the variables. Default diamond.
        neg_tail : string
            Description of the shape of the edge-tail indicating a
            negated variable. Default is 'odot'.
        column_distance : float
            Changes the distance between both partitions, measured in image-units.
            Default is 0.5

        Returns
        -------
        None, but outputs the files with the graph for each timestep.

        """
        _filename = self.outfolder / inc_file

        clausetag_n = var_name_one + '%d'
        vartag_n = var_name_two + '%d'

        g_incid = Graph(inc_file,
                        strict=True,
                        graph_attr={
                            'splines': 'false',
                            'ranksep': '0.2',
                            'nodesep': str(float(column_distance)),
                            'fontsize': str(int(fontsize)),
                            'compound': 'true'
                        },
                        edge_attr={
                            'penwidth': str(float(penwidth)),
                            'dir': 'back',
                            'arrowtail': 'none'
                        })
        with g_incid.subgraph(name='cluster_clause',
                              edge_attr={'style': 'invis'},
                              node_attr={
                                  'style': 'rounded,filled',
                                  'fillcolor': basefill
                              }) as clauses:
            clauses.attr(label='clauses')
            clauses.edges([(clausetag_n % (i + 1), clausetag_n % (i + 2))
                           for i in range(len(edges) - 1)])

        g_incid.attr('node',
                     shape=sndshape,
                     penwidth=str(float(penwidth)),
                     style='dotted')
        with g_incid.subgraph(name='cluster_ivar',
                              edge_attr={'style': 'invis'}) as ivars:
            ivars.attr(label='variables')
            ivars.edges([(vartag_n % (i + 1), vartag_n % (i + 2))
                         for i in range(num_vars - 1)])
            for i in range(num_vars):
                g_incid.node(vartag_n % (i + 1),
                             vartag_n % (i + 1),
                             color=colors[(i + 1) % len(colors)])

        g_incid.attr('edge', constraint='false')

        for clause in edges:
            for var in clause[1]:
                if var >= 0:
                    g_incid.edge(clausetag_n % clause[0],
                                 vartag_n % var,
                                 color=colors[var % len(colors)])
                else:
                    g_incid.edge(clausetag_n % clause[0],
                                 vartag_n % -var,
                                 color=colors[-var % len(colors)],
                                 arrowtail=neg_tail)

        # make edgelist variable-based (varX, clauseY), ...
        #  var_cl_iter [(1, 1), (4, 1), ...
        var_cl_iter = tuple(flatten([[(x, y[0]) for x in y[1]]
                                     for y in edges]))

        bodybaselen = len(g_incid.body)
        for i, variables in enumerate(timeline, start=1):  # all timesteps

            # reset highlighting
            g_incid.body = g_incid.body[:bodybaselen]
            if variables is None:
                g_incid.render(view=view,
                               format='svg',
                               filename=str(_filename) + str(i))
                continue

            emp_clause = {
                var_cl[1]
                for var_cl in var_cl_iter if abs(var_cl[0]) in variables
            }

            emp_var = {
                abs(var_cl[0])
                for var_cl in var_cl_iter if var_cl[1] in emp_clause
            }

            for var in emp_var:
                _vartag = vartag_n % abs(var)
                _style = 'solid,filled' if var in variables else 'dotted,filled'
                g_incid.node(_vartag,
                             _vartag,
                             style=_style,
                             fillcolor='yellow')

            for clause in emp_clause:
                g_incid.node(clausetag_n % clause,
                             clausetag_n % clause,
                             fillcolor='yellow')

            for edge in var_cl_iter:
                (var, clause) = edge

                _style = 'solid' if clause in emp_clause else 'dotted'
                _vartag = vartag_n % abs(var)

                if var >= 0:
                    g_incid.edge(clausetag_n % clause,
                                 _vartag,
                                 color=colors[var % len(colors)],
                                 style=_style)
                else:  # negated variable
                    g_incid.edge(clausetag_n % clause,
                                 _vartag,
                                 color=colors[-var % len(colors)],
                                 arrowtail='odot',
                                 style=_style)

            g_incid.render(view=view,
                           format='svg',
                           filename=str(_filename) + str(i))
Пример #4
0
    def general_graph(self,
                      timeline: Iterable[Optional[List[int]]],
                      edges: Iterable[Iterable[int]],
                      extra_nodes: Iterable[int] = tuple(),
                      view: bool = False,
                      fontsize: int = 20,
                      fontcolor: str = 'black',
                      penwidth: float = 2.2,
                      first_color: str = 'yellow',
                      first_style: str = 'filled',
                      second_color: str = 'green',
                      second_style: str = 'dotted,filled',
                      third_color: str = 'red',
                      graph_name: str = 'graph',
                      file_basename: str = 'graph',
                      do_sort_nodes: bool = True,
                      do_adj_nodes: bool = True,
                      var_name: str = '') -> None:
        """
        Creates one graph emphasized for the given timeline.

        Parameters
        ----------
        edges : Iterable of: {int, int}
            All edges between nodes in the graph.
            Should NOT contain self-edges!
            BOTH edges (x, y) and (y, x) could be in the edgelist.

        extra_nodes : Iterable of int
            Nodes that are probably not in the edges, but should be rendered.

        TIMELINE : Iterable of: None | [int...]
            None if no variables get highlighted in this step.
            Else the 'timeline' provides the set of variables that are
            in the bag(s) under consideration. This function computes all other
            variables that are involved in this timestep using the 'edgelist'.

        colors : Iterable of color
            Colors to use for the graph parts.

        Returns
        -------
        None, but outputs the files with the graph for each timestep.

        """
        _filename = self.outfolder / file_basename
        LOGGER.info("Generating general-graph for '%s'", file_basename)
        vartag_n: str = var_name + '%d'
        # sfdp http://yifanhu.net/SOFTWARE/SFDP/index.html
        default_engine = 'sfdp'

        graph = Graph(graph_name,
                      strict=True,
                      engine=default_engine,
                      graph_attr={
                          'fontsize': str(fontsize),
                          'overlap': 'false',
                          'outputorder': 'edgesfirst',
                          'K': '2'
                      },
                      node_attr={
                          'fontcolor': str(fontcolor),
                          'penwidth': str(penwidth),
                          'style': 'filled',
                          'fillcolor': 'white'
                      })

        if do_sort_nodes:
            bodybaselen = len(graph.body)
            # 1: layout with circo
            graph.engine = 'circo'
            # 2: nodes in edges+extra_nodes make a circle
            nodes = sorted([
                vartag_n % n
                for n in set(itertools.chain(flatten(edges), extra_nodes))
            ],
                           key=lambda x: (len(x), x))
            for i, node in enumerate(nodes):
                graph.edge(str(nodes[i - 1]), str(node))
            # 3: reads in bytes!
            code_lines = graph.pipe('plain').splitlines()
            # 4: save the (sorted) positions
            assert code_lines[0].startswith(b'graph')
            node_positions = [
                line.split()[1:4] for line in code_lines[1:]
                if line.startswith(b'node')
            ]
            # 5: cut layout
            graph.body = graph.body[:bodybaselen]
            for line in node_positions:
                graph.node(line[0].decode(),
                           pos='%f,%f!' % (float(line[1]), float(line[2])))
            # 6: Engine uses previous positions
            graph.engine = 'neato'

        for (src, tar) in edges:
            graph.edge(vartag_n % src, vartag_n % tar)
        for nodeid in extra_nodes:
            graph.node(vartag_n % nodeid)

        bodybaselen = len(graph.body)

        for i, variables in enumerate(timeline, start=1):  # all timesteps
            # reset highlighting
            graph.body = graph.body[:bodybaselen]

            if variables is None:
                graph.render(view=view,
                             format='svg',
                             filename=str(_filename) + str(i))
                continue

            for var in variables:
                graph.node(vartag_n % var,
                           fillcolor=first_color,
                           style=first_style)

            # highlight edges between variables
            for (s, t) in edges:
                if s in variables and t in variables:
                    graph.edge(vartag_n % s,
                               vartag_n % t,
                               color=third_color,
                               penwidth=str(penwidth))

            if do_adj_nodes:
                # set.difference accepts list as argument, "-" does not.
                edges = [set(edge) for edge in edges]
                adjacent = {
                    edge.difference(variables).pop()
                    for edge in edges if len(edge.difference(variables)) == 1
                }

                for var in adjacent:
                    graph.node(vartag_n % var,
                               color=second_color,
                               style=second_style)

            graph.render(view=view,
                         format='svg',
                         filename=str(_filename) + str(i))
Пример #5
0
    def read_timeline(self, edgearray) -> list:
        """
        Read from td_node_status and the edearray to
        - create the timeline of the solving process
        - construct the path and solution-tables used during solving.

        Parameters
        ----------
        edgearray : array of pairs of bagids
            Representing the tree-like structure between all bag-ids.
            It is assumed that all ids are included in this array.
            Example: [(2, 1), (3, 2), (4, 2), (5, 4)]

        Returns
        -------
        result : array
            array of bagids and eventually solution-tables.

        """
        with self.connection.cursor() as cur:  # create a cursor
            timeline = list()
            adj = convert_to_adj(edgearray) if self.intermed_nodes else {}
            order_solved = list(
                flatten(query_td_node_status_ordered(cur, self.problem)))
            # tour sol -> through result nodes along the edges

            if self.intermed_nodes:
                last = order_solved[-1]
                startpath = find_path(adj, last, order_solved[0])
                timeline = [[bag] for bag in startpath[1]]
            else:
                timeline.append([order_solved[0]])
            # add the other bags in order_solved to the timeline
            last = order_solved[0]
            for bag in order_solved:
                if self.intermed_nodes:
                    path = find_path(adj, last, bag)
                    for intermed in path[1][1:]:
                        timeline.append([intermed])
                # query column names
                column_names = list(
                    flatten(query_column_name(cur, self.problem, bag)))
                LOGGER.debug("column_names %s", column_names)
                # get solutions
                solution_raw = query_bag(cur, self.problem, bag)
                LOGGER.debug("solution_raw %s", solution_raw)
                # check for nulled variables - assuming whole columns are
                # nulled:
                columns_notnull = [
                    column_names[i] for i, x in enumerate(solution_raw[0])
                    if x is not None
                ]
                solution = [
                    bag,
                    [[
                        columns_notnull,
                        *[[int(v) for v in row if v is not None]
                          for row in solution_raw]
                    ], "sol bag " + str(bag),
                     self.footer(solution_raw), True]
                ]
                timeline.append(solution)
                last = bag
            return timeline
Пример #6
0
def test_flatten(arg, expected):
    """Test the flatten method."""
    assert list(flatten(arg)) == expected
Пример #7
0
def test_cant_flatten(arg):
    """Fail the flatten method with TypeError"""
    with raises(TypeError):
        list(flatten(arg))