Example #1
0
def PerfectMatchingPrinciple(G):
    """Generates the clauses for the graph perfect matching principle.

    The principle claims that there is a way to select edges to such
    that all vertices have exactly one incident edge set to 1.

    Parameters
    ----------
    G : undirected graph

    """
    cnf = CNF()

    # Describe the formula
    name = "Perfect Matching Principle"

    if hasattr(G, 'name'):
        cnf.header = name + " of graph:\n" + G.name + "\n" + cnf.header
    else:
        cnf.header = name + ".\n" + cnf.header

    def var_name(u, v):
        if u <= v:
            return 'x_{{{0},{1}}}'.format(u, v)
        else:
            return 'x_{{{0},{1}}}'.format(v, u)

    # Each vertex has exactly one edge set to one.
    for v in enumerate_vertices(G):

        edge_vars = [var_name(u, v) for u in neighbors(G, v)]
        cnf.add_equal_to(edge_vars, 1)

    return cnf
Example #2
0
    def transform_a_literal(self, polarity, varname):
        """Substitute a literal with a (negated) XOR

        Arguments:
        - `polarity`: polarity of the literal
        - `varname`: fariable to be substituted

        Returns: a list of clauses
        """
        varname = self._name_vertex_dict[varname]
        local_vars = neighbors(self._pattern, varname)
        local_names = ["Y_{{{0}}}".format(i) for i in local_vars]

        if self._function == 'xor':

            temp = CNF()
            temp.add_parity(local_names, 1 if polarity else 0)
            return list(temp)

        elif self._function == 'maj':

            threshold = (len(local_names)+1) // 2  # loose majority

            if polarity:
                return list(self.greater_or_equal_constraint(local_names, threshold))
            else:
                return list(self.less_than_constraint(local_names, threshold))

        else:
            raise RuntimeError(
                "Error: variable compression with invalid function")
Example #3
0
def PerfectMatchingPrinciple(G):
    """Generates the clauses for the graph perfect matching principle.
    
    The principle claims that there is a way to select edges to such
    that all vertices have exactly one incident edge set to 1.

    Parameters
    ----------
    G : undirected graph

    """
    cnf=CNF()

    # Describe the formula
    name="Perfect Matching Principle"
    
    if hasattr(G,'name'):
        cnf.header=name+" of graph:\n"+G.name+"\n"+cnf.header
    else:
        cnf.header=name+".\n"+cnf.header

    def var_name(u,v):
        if u<=v:
            return 'x_{{{0},{1}}}'.format(u,v)
        else:
            return 'x_{{{0},{1}}}'.format(v,u)
            
    # Each vertex has exactly one edge set to one.
    for v in enumerate_vertices(G):

        edge_vars = [var_name(u,v) for u in neighbors(G,v)]
        cnf.add_equal_to(edge_vars,1)

    return cnf
Example #4
0
    def transform_a_literal(self, polarity,varname):
        """Substitute a literal with a (negated) XOR

        Arguments:
        - `polarity`: polarity of the literal
        - `varname`: fariable to be substituted

        Returns: a list of clauses
        """
        varname = self._name_vertex_dict[varname]
        local_vars  = neighbors(self._pattern,varname)
        local_names = ["Y_{{{0}}}".format(i) for i in local_vars ]

        if self._function == 'xor':

            temp = CNF()
            temp.add_parity(local_names,1 if polarity else 0)
            return list(temp)

        elif self._function == 'maj':

            threshold = (len(local_names)+1) // 2 # loose majority

            if polarity:
                return list(self.greater_or_equal_constraint(local_names, threshold ))
            else:
                return list(self.less_than_constraint(local_names, threshold ))
            
        else:
            raise RuntimeError("Error: variable compression with invalid function")
Example #5
0
def TseitinFormula(graph,charges=None):
    """Build a Tseitin formula based on the input graph.

    Odd charge is put on the first vertex by default, unless other
    vertices are is specified in input.

    Arguments:
    - `graph`: input graph
    - `charges': odd or even charge for each vertex
    """
    V=enumerate_vertices(graph)

    if charges==None:
        charges=[1]+[0]*(len(V)-1)             # odd charge on first vertex
    else:
        charges = [bool(c) for c in charges]   # map to boolean

    if len(charges)<len(V):
        charges=charges+[0]*(len(V)-len(charges))  # pad with even charges

    # init formula
    tse=CNF()
    for e in sorted(graph.edges(),key=sorted):
        tse.add_variable(edgename(e))

    # add constraints
    for v,c in zip(V,charges):
        
        # produce all clauses and save half of them
        names = [ edgename((u,v)) for u in neighbors(graph,v) ]
        for cls in parity_constraint(names,c):
            tse.add_clause(list(cls),strict=True)

    return tse
Example #6
0
def EvenColoringFormula(G):
    """Even coloring formula

    The formula is defined on a graph :math:`G` and claims that it is
    possible to split the edges of the graph in two parts, so that
    each vertex has an equal number of incident edges in each part.

    The formula is defined on graphs where all vertices have even
    degree. The formula is satisfiable only on those graphs with an
    even number of vertices in each connected component [1]_.

    Arguments
    ---------
    G : networkx.Graph 
       a simple undirected graph where all vertices have even degree

    Raises
    ------
    ValueError
       if the graph in input has a vertex with odd degree

    Returns
    -------
    CNF object

    References
    ----------
    .. [1] Locality and Hard SAT-instances, Klas Markstrom
       Journal on Satisfiability, Boolean Modeling and Computation 2 (2006) 221-228

    """
    F = CNF()
    F.mode_strict()
    F.header = "Even coloring formula on graph " + G.name + "\n" + F.header

    def var_name(u, v):
        if u <= v:
            return 'x_{{{0},{1}}}'.format(u, v)
        else:
            return 'x_{{{0},{1}}}'.format(v, u)

    for (u, v) in enumerate_edges(G):
        F.add_variable(var_name(u, v))

    # Defined on both side
    for v in enumerate_vertices(G):

        if G.degree(v) % 2 == 1:
            raise ValueError(
                "Markstrom formulas requires all vertices to have even degree."
            )

        edge_vars = [var_name(u, v) for u in neighbors(G, v)]

        # F.add_exactly_half_floor would work the same
        F.add_exactly_half_ceil(edge_vars)

    return F
Example #7
0
def EvenColoringFormula(G):
    """Even coloring formula

    The formula is defined on a graph :math:`G` and claims that it is
    possible to split the edges of the graph in two parts, so that
    each vertex has an equal number of incident edges in each part.

    The formula is defined on graphs where all vertices have even
    degree. The formula is satisfiable only on those graphs with an
    even number of vertices in each connected component [1]_.

    Arguments
    ---------
    G : networkx.Graph 
       a simple undirected graph where all vertices have even degree

    Raises
    ------
    ValueError
       if the graph in input has a vertex with odd degree

    Returns
    -------
    CNF object

    References
    ----------
    .. [1] Locality and Hard SAT-instances, Klas Markstrom
       Journal on Satisfiability, Boolean Modeling and Computation 2 (2006) 221-228

    """
    F = CNF()
    F.header = "Even coloring formula on graph " + G.name + "\n" + F.header

    def var_name(u,v):
        if u<=v:
            return 'x_{{{0},{1}}}'.format(u,v)
        else:
            return 'x_{{{0},{1}}}'.format(v,u)
    
    for (u, v) in enumerate_edges(G):
        F.add_variable(var_name(u, v))

    # Defined on both side
    for v in enumerate_vertices(G):

        if G.degree(v) % 2 == 1:
            raise ValueError("Markstrom formulas requires all vertices to have even degree.")

        edge_vars = [ var_name(u,v) for u in neighbors(G,v) ]
        
        for cls in CNF.equal_to_constraint(edge_vars,
                                           len(edge_vars)/2):
            F.add_clause(cls,strict=True)

    return F
Example #8
0
def TseitinFormula(graph,charges=None):
    """Build a Tseitin formula based on the input graph.

    Odd charge is put on the first vertex by default, unless other
    vertices are is specified in input.

    Arguments:
    - `graph`: input graph
    - `charges': odd or even charge for each vertex
    """
    V=enumerate_vertices(graph)

    if charges==None:
        charges=[1]+[0]*(len(V)-1)             # odd charge on first vertex
    else:
        charges = [bool(c) for c in charges]   # map to boolean

    if len(charges)<len(V):
        charges=charges+[0]*(len(V)-len(charges))  # pad with even charges

    # init formula
    tse=CNF()
    edgename = { }
    
    for (u,v) in sorted(graph.edges(),key=sorted):
        edgename[(u,v)] =  "E_{{{0},{1}}}".format(u,v)
        edgename[(v,u)] =  "E_{{{0},{1}}}".format(u,v)
        tse.add_variable(edgename[(u,v)])

    # add constraints
    for v,c in zip(V,charges):
        
        # produce all clauses and save half of them
        names = [ edgename[(u,v)] for u in neighbors(graph,v) ]
        for cls in CNF.parity_constraint(names,c):
            tse.add_clause(list(cls),strict=True)

    return tse
Example #9
0
def TseitinFormula(graph, charges=None, encoding=None):
    """Build a Tseitin formula based on the input graph.

    Odd charge is put on the first vertex by default, unless other
    vertices are is specified in input.

    Arguments:
    - `graph`: input graph
    - `charges': odd or even charge for each vertex
    """
    V = enumerate_vertices(graph)

    if charges == None:
        charges = [1] + [0] * (len(V) - 1)  # odd charge on first vertex
    else:
        charges = [bool(c) for c in charges]  # map to boolean

    if len(charges) < len(V):
        charges = charges + [0] * (len(V) - len(charges)
                                   )  # pad with even charges

    # init formula
    tse = CNF()
    edgename = {}

    for (u, v) in sorted(graph.edges(), key=sorted):
        edgename[(u, v)] = "E_{{{0},{1}}}".format(u, v)
        edgename[(v, u)] = "E_{{{0},{1}}}".format(u, v)
        tse.add_variable(edgename[(u, v)])

    tse.mode_strict()
    # add constraints
    for v, charge in zip(V, charges):

        # produce all clauses and save half of them
        names = [edgename[(u, v)] for u in neighbors(graph, v)]
        if encoding == None:
            tse.add_parity(names, charge)
        else:

            def toArgs(listOfTuples, operator, degree):
                return list(sum(listOfTuples, ())) + [operator, degree]

            terms = list(map(lambda x: (1, x), names))
            w = len(terms)
            k = w // 2

            if encoding == "extendedPBAnyHelper":
                for i in range(k):
                    helper = ("xor_helper", i, v)
                    tse.add_variable(helper)
                    terms.append((2, helper))
            elif encoding == "extendedPBOneHelper":
                helpers = list()
                for i in range(k):
                    helper = ("xor_helper", i, v)
                    helpers.append(helper)
                    tse.add_variable(helper)
                    terms.append(((i + 1) * 2, helper))
                tse.add_linear(*toArgs([(1, x) for x in helpers], "<=", 1))
            elif encoding == "extendedPBExpHelper":
                for i in range(math.ceil(math.log(k, 2)) + 1):
                    helper = ("xor_helper", i, v)
                    tse.add_variable(helper)
                    terms.append((2**i * 2, helper))
            else:
                raise ValueError("Invalid encoding '%s'" % encoding)

            degree = 2 * k + (charge % 2)
            tse.add_linear(*toArgs(terms, "==", degree))

    return tse
Example #10
0
def GraphPigeonholePrinciple(graph,functional=False,onto=False):
    """Graph Pigeonhole Principle CNF formula

    The graph pigeonhole principle CNF formula, defined on a bipartite
    graph G=(L,R,E), claims that there is a subset E' of the edges such that 
    every vertex on the left size L has at least one incident edge in E' and 
    every edge on the right side R has at most one incident edge in E'.

    This is possible only if the graph has a matching of size |L|.

    There are different variants of this formula, depending on the
    values of `functional` and `onto` argument.

    - PHP(G):  each left vertex can be incident to multiple edges in E'
    - FPHP(G): each left vertex must be incident to exaclty one edge in E'
    - onto-PHP: all right vertices must be incident to some vertex
    - matching: E' must be a perfect matching between L and R

    Arguments:
    - `graph` : bipartite graph
    - `functional`: add clauses to enforce at most one edge per left vertex
    - `onto`: add clauses to enforce that any right vertex has one incident edge


    Remark: the graph vertices must have the 'bipartite' attribute
    set. Left vertices must have it set to 0 and the right ones to
    1. A KeyException is raised otherwise.

    """

    def var_name(p,h):
        return 'p_{{{0},{1}}}'.format(p,h)

    if functional:
        if onto:
            formula_name="Graph matching"
        else:
            formula_name="Graph functional pigeonhole principle"
    else:
        if onto:
            formula_name="Graph onto pigeonhole principle"
        else:
            formula_name="Graph pigeonhole principle"


    gphp=CNF()
    gphp.header="{0} formula for graph {1}\n".format(formula_name,graph.name)

    Left, _ = bipartite_sets(graph)

    for p in Left:
        for h in neighbors(graph,p):
            gphp.add_variable(var_name(p,h))

    clauses=gphp.sparse_mapping( graph,
                                 var_name=var_name,
                                 complete = True,
                                 injective = True,
                                 functional = functional,
                                 surjective = onto)

    for c in clauses:
        gphp.add_clause(c,strict=True)

    return gphp
Example #11
0
def SubsetCardinalityFormula(B, equalities=False):
    r"""SubsetCardinalityFormula

    Consider a bipartite graph :math:`B`. The CNF claims that at least half
    of the edges incident to each of the vertices on left side of :math:`B`
    must be zero, while at least half of the edges incident to each
    vertex on the left side must be one.

    Variants of these formula on specific families of bipartite graphs
    have been studied in [1]_, [2]_ and [3]_, and turned out to be
    difficult for resolution based SAT-solvers.

    Each variable of the formula is denoted as :math:`x_{i,j}` where
    :math:`\{i,j\}` is an edge of the bipartite graph. The clauses of
    the CNF encode the following constraints on the edge variables.

    For every left vertex i with neighborhood :math:`\Gamma(i)`

    .. math::

         \sum_{j \in \Gamma(i)} x_{i,j} \geq \frac{|\Gamma(i)|}{2}

    For every right vertex j with neighborhood :math:`\Gamma(j)`

    .. math::

         \sum_{i \in \Gamma(j)} x_{i,j} \leq \frac{|\Gamma(j)|}{2}.

    If the ``equalities`` flag is true, the constraints are instead
    represented by equations.

    .. math::

         \sum_{j \in \Gamma(i)} x_{i,j} = \left\lceil \frac{|\Gamma(i)|}{2} \right\rceil

    .. math::

         \sum_{i \in \Gamma(j)} x_{i,j} = \left\lfloor \frac{|\Gamma(j)|}{2} \right\rfloor .

    Parameters
    ----------
    B : networkx.Graph
        the graph vertices must have the 'bipartite' attribute
        set. Left vertices must have it set to 0 and the right ones to 1.
        A KeyException is raised otherwise.

    equalities : boolean
        use equations instead of inequalities to express the
        cardinality constraints.  (default: False) 

    Returns
    -------
    A CNF object

    References
    ----------
    .. [1] Mladen Miksa and Jakob Nordstrom
           Long proofs of (seemingly) simple formulas
           Theory and Applications of Satisfiability Testing--SAT 2014 (2014)
    .. [2] Ivor Spence
           sgen1: A generator of small but difficult satisfiability benchmarks
           Journal of Experimental Algorithmics (2010)
    .. [3] Allen Van Gelder and Ivor Spence
           Zero-One Designs Produce Small Hard SAT Instances
           Theory and Applications of Satisfiability Testing--SAT 2010(2010)

    """
    Left, Right = bipartite_sets(B)

    ssc = CNF()
    ssc.header = "Subset cardinality formula for graph {0}\n".format(B.name)
    ssc.mode_strict()

    def var_name(u, v):
        """Compute the variable names."""
        if u <= v:
            return 'x_{{{0},{1}}}'.format(u, v)
        else:
            return 'x_{{{0},{1}}}'.format(v, u)

    for u in Left:
        for v in neighbors(B, u):
            ssc.add_variable(var_name(u, v))

    for u in Left:
        edge_vars = [var_name(u, v) for v in neighbors(B, u)]

        if equalities:
            ssc.add_exactly_half_ceil(edge_vars)
        else:
            ssc.add_loose_majority(edge_vars)

    for v in Right:
        edge_vars = [var_name(u, v) for u in neighbors(B, v)]

        if equalities:
            ssc.add_exactly_half_floor(edge_vars)
        else:
            ssc.add_loose_minority(edge_vars)

    return ssc
Example #12
0
def SubsetCardinalityFormula(B, equalities=False):
    r"""SubsetCardinalityFormula

    Consider a bipartite graph :math:`B`. The CNF claims that at least half
    of the edges incident to each of the vertices on left side of :math:`B`
    must be zero, while at least half of the edges incident to each
    vertex on the left side must be one.

    Variants of these formula on specific families of bipartite graphs
    have been studied in [1]_, [2]_ and [3]_, and turned out to be
    difficult for resolution based SAT-solvers.

    Each variable of the formula is denoted as :math:`x_{i,j}` where
    :math:`\{i,j\}` is an edge of the bipartite graph. The clauses of
    the CNF encode the following constraints on the edge variables.

    For every left vertex i with neighborhood :math:`\Gamma(i)`

    .. math::
         
         \sum_{j \in \Gamma(i)} x_{i,j} \geq \frac{|\Gamma(i)|}{2}

    For every right vertex j with neighborhood :math:`\Gamma(j)`

    .. math::
         
         \sum_{i \in \Gamma(j)} x_{i,j} \leq \frac{|\Gamma(j)|}{2}.

    If the ``equalities`` flag is true, the constraints are instead
    represented by equations.
    
    .. math::
         
         \sum_{j \in \Gamma(i)} x_{i,j} = \left\lceil \frac{|\Gamma(i)|}{2} \right\rceil

    .. math::
         
         \sum_{i \in \Gamma(j)} x_{i,j} = \left\lfloor \frac{|\Gamma(j)|}{2} \right\rfloor .

    Parameters
    ----------
    B : networkx.Graph
        the graph vertices must have the 'bipartite' attribute
        set. Left vertices must have it set to 0 and the right ones to 1.
        A KeyException is raised otherwise.

    equalities : boolean
        use equations instead of inequalities to express the
        cardinality constraints.  (default: False) 

    Returns
    -------
    A CNF object

    References
    ----------
    .. [1] Mladen Miksa and Jakob Nordstrom
           Long proofs of (seemingly) simple formulas
           Theory and Applications of Satisfiability Testing--SAT 2014 (2014)
    .. [2] Ivor Spence
           sgen1: A generator of small but difficult satisfiability benchmarks
           Journal of Experimental Algorithmics (2010)
    .. [3] Allen Van Gelder and Ivor Spence
           Zero-One Designs Produce Small Hard SAT Instances
           Theory and Applications of Satisfiability Testing--SAT 2010(2010)

    """
    Left, Right = bipartite_sets(B)

    ssc = CNF()
    ssc.header = "Subset cardinality formula for graph {0}\n".format(B.name)

    def var_name(u, v):
        """Compute the variable names."""
        if u <= v:
            return "x_{{{0},{1}}}".format(u, v)
        else:
            return "x_{{{0},{1}}}".format(v, u)

    for u in Left:
        for v in neighbors(B, u):
            ssc.add_variable(var_name(u, v))

    for u in Left:
        edge_vars = [var_name(u, v) for v in neighbors(B, u)]

        if equalities:
            for cls in CNF.exactly_half_ceil(edge_vars):
                ssc.add_clause(cls, strict=True)
        else:
            for cls in CNF.loose_majority_constraint(edge_vars):
                ssc.add_clause(cls, strict=True)

    for v in Right:
        edge_vars = [var_name(u, v) for u in neighbors(B, v)]

        if equalities:
            for cls in CNF.exactly_half_floor(edge_vars):
                ssc.add_clause(cls, strict=True)
        else:
            for cls in CNF.loose_minority_constraint(edge_vars):
                ssc.add_clause(cls, strict=True)

    return ssc