예제 #1
0
def BinaryPigeonholePrinciple(pigeons,holes):
    """Binary Pigeonhole Principle CNF formula

    The pigeonhole principle claims that no M pigeons can sit in
    N pigeonholes without collision if M>N. This formula encodes the
    principle using binary strings to identify the holes.

    Parameters
    ----------
    pigeon : int 
       number of pigeons
    holes : int 
       number of holes
    """

    bphp=CNF()
    bphp.header="Binary Pigeonhole Principle for {0} pigeons and {1} holes\n".format(pigeons,holes)\
                 + bphp.header
    
    bphpgen=bphp.binary_mapping(xrange(1,pigeons+1), xrange(1,holes+1), injective = True)

    for v in bphpgen.variables():
        bphp.add_variable(v)
        
    for c in bphpgen.clauses():
        bphp.add_clause_unsafe(c)

    return bphp
예제 #2
0
def BinaryPigeonholePrinciple(pigeons, holes):
    """Binary Pigeonhole Principle CNF formula

    The pigeonhole principle claims that no M pigeons can sit in
    N pigeonholes without collision if M>N. This formula encodes the
    principle using binary strings to identify the holes.

    Parameters
    ----------
    pigeon : int 
       number of pigeons
    holes : int 
       number of holes
    """

    bphp = CNF()
    bphp.header="Binary Pigeonhole Principle for {0} pigeons and {1} holes\n".format(pigeons,holes)\
                 + bphp.header

    bphpgen = bphp.binary_mapping(xrange(1, pigeons + 1),
                                  xrange(1, holes + 1),
                                  injective=True)

    for v in bphpgen.variables():
        bphp.add_variable(v)

    for c in bphpgen.clauses():
        bphp.add_clause_unsafe(c)

    return bphp
예제 #3
0
def PebblingFormula(digraph):
    """Pebbling formula

    Build a pebbling formula from the directed graph. If the graph has
    an `ordered_vertices` attribute, then it is used to enumerate the
    vertices (and the corresponding variables).

    Arguments:
    - `digraph`: directed acyclic graph.
    """
    if not is_dag(digraph):
        raise ValueError(
            "Pebbling formula is defined only for directed acyclic graphs")

    peb = CNF()

    if hasattr(digraph, 'name'):
        peb.header = "Pebbling formula of: " + digraph.name + "\n\n" + peb.header
    else:
        peb.header = "Pebbling formula\n\n" + peb.header

    # add variables in the appropriate order
    vertices = enumerate_vertices(digraph)
    position = dict((v, i) for (i, v) in enumerate(vertices))

    for v in vertices:
        peb.add_variable(
            v, description="There is a pebble on vertex ${}$".format(v))

    # add the clauses
    for v in vertices:

        # If predecessors are pebbled the vertex must be pebbled
        pred = sorted(digraph.predecessors(v), key=lambda x: position[x])
        peb.add_clause_unsafe([(False, p) for p in pred] + [(True, v)])

        if digraph.out_degree(v) == 0:  #the sink
            peb.add_clause_unsafe([(False, v)])

    return peb
예제 #4
0
def PebblingFormula(digraph):
    """Pebbling formula

    Build a pebbling formula from the directed graph. If the graph has
    an `ordered_vertices` attribute, then it is used to enumerate the
    vertices (and the corresponding variables).

    Arguments:
    - `digraph`: directed acyclic graph.
    """
    if not is_dag(digraph):
        raise ValueError("Pebbling formula is defined only for directed acyclic graphs")

    peb=CNF()

    if hasattr(digraph,'name'):
        peb.header="Pebbling formula of: "+digraph.name+"\n\n"+peb.header
    else:
        peb.header="Pebbling formula\n\n"+peb.header

    # add variables in the appropriate order
    vertices=enumerate_vertices(digraph)
    position=dict((v,i) for (i,v) in enumerate(vertices))

    for v in vertices:
        peb.add_variable(v,description="There is a pebble on vertex ${}$".format(v))

    # add the clauses
    for v in vertices:

        # If predecessors are pebbled the vertex must be pebbled
        pred=sorted(digraph.predecessors(v),key=lambda x:position[x])
        peb.add_clause_unsafe([(False,p) for p in pred]+[(True,v)])

        if digraph.out_degree(v)==0: #the sink
            peb.add_clause_unsafe([(False,v)])

    return peb
예제 #5
0
def StoneFormula(D, nstones):
    """Stone formulas

    The stone formulas have been introduced in [2]_ and generalized in
    [1]_. They are one of the classic examples that separate regular
    resolutions from general resolution [1]_.

    A \"Stones formula\" from a directed acyclic graph :math:`D`
    claims that each vertex of the graph is associated with one on
    :math:`s` stones (not necessarily in an injective way).
    In particular for each vertex :math:`v` in :math:`V(D)` and each
    stone :math:`j` we have a variable :math:`P_{v,j}` that claims
    that stone :math:`j` is associated to vertex :math:`v`.

    Each stone can be either red or blue, and not both.
    The propositional variable :math:`R_j` if true when the stone
    :math:`j` is red and false otherwise.

    The clauses of the formula encode the following constraints.
    If a stone is on a source vertex (i.e. a vertex with no incoming
    edges), then it must be red. If all stones on the predecessors of
    a vertex are red, then the stone of the vertex itself must be red.

    The formula furthermore enforces that the stones on the sinks
    (i.e. vertices with no outgoing edges) are blue.

    .. note:: The exact formula structure depends by the graph and on
              its topological order, which is determined by the
              ``enumerate_vertices(D)``.

    Parameters
    ----------
    D : a directed acyclic graph
        it should be a directed acyclic graph.
    nstones : int
       the number of stones.

    Raises
    ------
    ValueError
       if :math:`D` is not a directed acyclic graph
    
    ValueError
       if the number of stones is negative

    References
    ----------
    .. [1] M. Alekhnovich, J. Johannsen, T. Pitassi and A. Urquhart
    	   An Exponential Separation between Regular and General Resolution.
           Theory of Computing (2007)
    .. [2] R. Raz and P. McKenzie
           Separation of the monotone NC hierarchy.
           Combinatorica (1999)

    """
    if not is_dag(D):
        raise ValueError(
            "Stone formulas are defined only for directed acyclic graphs.")

    if nstones < 0:
        raise ValueError("There must be at least one stone.")

    cnf = CNF()

    if hasattr(D, 'name'):
        cnf.header = "Stone formula of: " + D.name + "\nwith " + str(
            nstones) + " stones\n" + cnf.header
    else:
        cnf.header = "Stone formula with " + str(
            nstones) + " stones\n" + cnf.header

    # Add variables in the appropriate order
    vertices = enumerate_vertices(D)
    position = dict((v, i) for (i, v) in enumerate(vertices))
    stones = list(range(1, nstones + 1))

    # Caching variable names
    color_vn = {}
    stone_vn = {}

    # Stones->Vertices variables
    for v in vertices:
        for j in stones:
            stone_vn[(v, j)] = "P_{{{0},{1}}}".format(v, j)
            cnf.add_variable(stone_vn[(v, j)],
                             description="Stone ${1}$ on vertex ${0}$".format(
                                 v, j))

    # Color variables
    for j in stones:
        color_vn[j] = "R_{{{0}}}".format(j)
        cnf.add_variable(color_vn[j],
                         description="Stone ${}$ is red".format(j))

    # Each vertex has some stone
    for v in vertices:
        cnf.add_clause_unsafe([(True, stone_vn[(v, j)]) for j in stones])

    # If predecessors have red stones, the sink must have a red stone
    for v in vertices:
        for j in stones:
            pred = sorted(D.predecessors(v), key=lambda x: position[x])
            for stones_tuple in product([s for s in stones if s != j],
                                        repeat=len(pred)):
                cnf.add_clause_unsafe([(False, stone_vn[(p, s)])
                                       for (p, s) in zip(pred, stones_tuple)] +
                                      [(False, stone_vn[(v, j)])] +
                                      [(False, color_vn[s])
                                       for s in _uniqify_list(stones_tuple)] +
                                      [(True, color_vn[j])])

        if D.out_degree(v) == 0:  #the sink
            for j in stones:
                cnf.add_clause_unsafe([(False, stone_vn[(v, j)]),
                                       (False, color_vn[j])])

    return cnf
예제 #6
0
def StoneFormula(D,nstones):
    """Stone formulas

    The stone formulas have been introduced in [2]_ and generalized in
    [1]_. They are one of the classic examples that separate regular
    resolutions from general resolution [1]_.

    A \"Stones formula\" from a directed acyclic graph :math:`D`
    claims that each vertex of the graph is associated with one on
    :math:`s` stones (not necessarily in an injective way).
    In particular for each vertex :math:`v` in :math:`V(D)` and each
    stone :math:`j` we have a variable :math:`P_{v,j}` that claims
    that stone :math:`j` is associated to vertex :math:`v`.

    Each stone can be either red or blue, and not both.
    The propositional variable :math:`R_j` if true when the stone
    :math:`j` is red and false otherwise.

    The clauses of the formula encode the following constraints.
    If a stone is on a source vertex (i.e. a vertex with no incoming
    edges), then it must be red. If all stones on the predecessors of
    a vertex are red, then the stone of the vertex itself must be red.

    The formula furthermore enforces that the stones on the sinks
    (i.e. vertices with no outgoing edges) are blue.

    .. note:: The exact formula structure depends by the graph and on
              its topological order, which is determined by the
              ``enumerate_vertices(D)``.

    Parameters
    ----------
    D : a directed acyclic graph
        it should be a directed acyclic graph.
    nstones : int
       the number of stones.

    Raises
    ------
    ValueError
       if :math:`D` is not a directed acyclic graph
    
    ValueError
       if the number of stones is negative

    References
    ----------
    .. [1] M. Alekhnovich, J. Johannsen, T. Pitassi and A. Urquhart
    	   An Exponential Separation between Regular and General Resolution.
           Theory of Computing (2007)
    .. [2] R. Raz and P. McKenzie
           Separation of the monotone NC hierarchy.
           Combinatorica (1999)

    """
    if not is_dag(D):
        raise ValueError("Stone formulas are defined only for directed acyclic graphs.")
    
    if nstones<0:
        raise ValueError("There must be at least one stone.")

    cnf = CNF()

    if hasattr(D, 'name'):
        cnf.header = "Stone formula of: " + D.name + "\nwith " + str(nstones) + " stones\n" + cnf.header
    else:
        cnf.header = "Stone formula with " + str(nstones) + " stones\n" + cnf.header

    # Add variables in the appropriate order
    vertices=enumerate_vertices(D)
    position=dict((v,i) for (i,v) in enumerate(vertices))
    stones=range(1,nstones+1)

    # Caching variable names
    color_vn = {}
    stone_vn = {}
    
    # Stones->Vertices variables
    for v in vertices:
        for j in stones:
            stone_vn[(v,j)] = "P_{{{0},{1}}}".format(v,j) 
            cnf.add_variable(stone_vn[(v,j)],
                             description="Stone ${1}$ on vertex ${0}$".format(v,j))

    # Color variables
    for j in stones:
        color_vn[j] = "R_{{{0}}}".format(j)
        cnf.add_variable(color_vn[j],
                         description="Stone ${}$ is red".format(j))
    
    # Each vertex has some stone
    for v in vertices:
        cnf.add_clause_unsafe([(True,stone_vn[(v,j)]) for j in stones])
        
    # If predecessors have red stones, the sink must have a red stone
    for v in vertices:
        for j in stones:
            pred=sorted(D.predecessors(v),key=lambda x:position[x])
            for stones_tuple in product([s for s in stones if s!=j],repeat=len(pred)):
                cnf.add_clause_unsafe([(False, stone_vn[(p,s)]) for (p,s) in zip(pred,stones_tuple)] +
                                      [(False, stone_vn[(v,j)])] +
                                      [(False, color_vn[s]) for s in _uniqify_list(stones_tuple)] +
                                      [(True,  color_vn[j])])
        
        if D.out_degree(v)==0: #the sink
            for j in stones:
                cnf.add_clause_unsafe([ (False,stone_vn[(v,j)]), (False,color_vn[j])])

    return cnf
예제 #7
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, Right = bipartite_sets(graph)

    mapping = gphp.unary_mapping(Left,Right,
                                 sparsity_pattern=graph,
                                 var_name=var_name,
                                 injective = True,
                                 functional = functional,
                                 surjective = onto)
    
    for v in mapping.variables():
        gphp.add_variable(v)

    for c in mapping.clauses():
        gphp.add_clause_unsafe(c)

    return gphp
예제 #8
0
def PigeonholePrinciple(pigeons,holes,functional=False,onto=False):
    """Pigeonhole Principle CNF formula

    The pigeonhole  principle claims  that no M  pigeons can sit  in N
    pigeonholes  without collision  if M>N.   The  counterpositive CNF
    formulation  requires  such mapping  to  be  satisfied. There  are
    different  variants of this  formula, depending  on the  values of
    `functional` and `onto` argument.

    - PHP: pigeon can sit in multiple holes
    - FPHP: each pigeon sits in exactly one hole
    - onto-PHP: pigeon can  sit in multiple holes, every  hole must be
                covered.
    - Matching: one-to-one bijection between pigeons and holes.

    Arguments:
    - `pigeon`: number of pigeons
    - `hole`:   number of holes
    - `functional`: add clauses to enforce at most one hole per pigeon
    - `onto`: add clauses to enforce that any hole must have a pigeon

    >>> print(PigeonholePrinciple(4,3).dimacs(export_header=False))
    p cnf 12 22
    1 2 3 0
    4 5 6 0
    7 8 9 0
    10 11 12 0
    -1 -4 0
    -1 -7 0
    -1 -10 0
    -4 -7 0
    -4 -10 0
    -7 -10 0
    -2 -5 0
    -2 -8 0
    -2 -11 0
    -5 -8 0
    -5 -11 0
    -8 -11 0
    -3 -6 0
    -3 -9 0
    -3 -12 0
    -6 -9 0
    -6 -12 0
    -9 -12 0
    """

    def var_name(p,h):
        return 'p_{{{0},{1}}}'.format(p,h)
    
    if functional:
        if onto:
            formula_name="Matching"
        else:
            formula_name="Functional pigeonhole principle"
    else:
        if onto:
            formula_name="Onto pigeonhole principle"
        else:
            formula_name="Pigeonhole principle"
            
    php=CNF()
    php.header="{0} formula for {1} pigeons and {2} holes\n".format(formula_name,pigeons,holes)\
        + php.header

    
    mapping=php.unary_mapping(
        xrange(1,pigeons+1),
        xrange(1,holes+1),
        var_name=var_name,
        injective = True,
        functional = functional,
        surjective = onto)

    for v in mapping.variables():
        php.add_variable(v)

    for c in mapping.clauses():
        php.add_clause_unsafe(c)

    return php
예제 #9
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, Right = bipartite_sets(graph)

    mapping = gphp.unary_mapping(Left,
                                 Right,
                                 sparsity_pattern=graph,
                                 var_name=var_name,
                                 injective=True,
                                 functional=functional,
                                 surjective=onto)

    for v in mapping.variables():
        gphp.add_variable(v)

    for c in mapping.clauses():
        gphp.add_clause_unsafe(c)

    return gphp
예제 #10
0
def PigeonholePrinciple(pigeons, holes, functional=False, onto=False):
    """Pigeonhole Principle CNF formula

    The pigeonhole  principle claims  that no M  pigeons can sit  in N
    pigeonholes  without collision  if M>N.   The  counterpositive CNF
    formulation  requires  such mapping  to  be  satisfied. There  are
    different  variants of this  formula, depending  on the  values of
    `functional` and `onto` argument.

    - PHP: pigeon can sit in multiple holes
    - FPHP: each pigeon sits in exactly one hole
    - onto-PHP: pigeon can  sit in multiple holes, every  hole must be
                covered.
    - Matching: one-to-one bijection between pigeons and holes.

    Arguments:
    - `pigeon`: number of pigeons
    - `hole`:   number of holes
    - `functional`: add clauses to enforce at most one hole per pigeon
    - `onto`: add clauses to enforce that any hole must have a pigeon

    >>> print(PigeonholePrinciple(4,3).dimacs(export_header=False))
    p cnf 12 22
    1 2 3 0
    4 5 6 0
    7 8 9 0
    10 11 12 0
    -1 -4 0
    -1 -7 0
    -1 -10 0
    -4 -7 0
    -4 -10 0
    -7 -10 0
    -2 -5 0
    -2 -8 0
    -2 -11 0
    -5 -8 0
    -5 -11 0
    -8 -11 0
    -3 -6 0
    -3 -9 0
    -3 -12 0
    -6 -9 0
    -6 -12 0
    -9 -12 0
    """
    def var_name(p, h):
        return 'p_{{{0},{1}}}'.format(p, h)

    if functional:
        if onto:
            formula_name = "Matching"
        else:
            formula_name = "Functional pigeonhole principle"
    else:
        if onto:
            formula_name = "Onto pigeonhole principle"
        else:
            formula_name = "Pigeonhole principle"

    php = CNF()
    php.header="{0} formula for {1} pigeons and {2} holes\n".format(formula_name,pigeons,holes)\
        + php.header

    mapping = php.unary_mapping(xrange(1, pigeons + 1),
                                xrange(1, holes + 1),
                                var_name=var_name,
                                injective=True,
                                functional=functional,
                                surjective=onto)

    for v in mapping.variables():
        php.add_variable(v)

    for c in mapping.clauses():
        php.add_clause_unsafe(c)

    return php