简体   繁体   English

找到一组边以添加到一组图中以满足连通性约束

[英]Finding a set of edges to add to a set of graphs to satisfy connectivity constraints

I have a set of N undirected, disconnected graphs that share the same M vertices, but have different edges.我有一组 N 个无向、断开连接的图,它们共享相同的 M 个顶点,但具有不同的边。 I also have a set of constraints for each graph of the form "V 0 is connected to V 1 " or "V 3 is not connected to V 5 " etc.对于“V 0连接到 V 1 ”或“V 3未连接到 V 5 ”等形式的每个图形,我也有一组约束。

I want to find a set of edges such that adding those edges to every graph causes every graph to satisfy its constraints.我想找到一组边,以便将这些边添加到每个图中会导致每个图都满足其约束。

For a simple example, consider, for two given graphs with vertices V 0 , V 1 , V 2 , V 3 , V 4 :举一个简单的例子,考虑两个顶点为 V 0 , V 1 , V 2 , V 3 , V 4的图:

  • a graph G with one edge (V 0 , V 1 ) and the constraint "V 0 is connected to V 2 "具有一条边 (V 0 , V 1 ) 和约束“V 0连接到 V 2 ”的图 G
  • a graph H with edge (V 1 , V 3 ) and the constraints "V 2 is connected to V 4 ", "V 0 is not connected to V 2 "带有边 (V 1 , V 3 ) 和约束“V 2连接到 V 4 ”、“V 0连接到 V 2 ”的图 H

With these given parameters, the problem is satisfiable by adding the edges {(V 1 , V 2 ), (V 3 , V 4 )} to both of the graphs.使用这些给定的参数,可以通过将边 {(V 1 , V 2 ), (V 3 , V 4 )} 添加到两个图中来解决问题。


After failing to solve the problem with a script, I reached for z3 to help, but I've run into trouble trying to express connectivity.在未能用脚本解决问题后,我寻求 z3 提供帮助,但我在尝试表达连接时遇到了麻烦。 My current solution consists of an unquantified formula with only boolean terms:我目前的解决方案包括一个只有布尔项的未量化公式:

  • c i,j,k for each pair of vertices i, j representing whether they are connected in graph k with added edges from the solution c i,j,k对于每对顶点i,j表示它们是否在图k中连接并添加了来自解的边
  • g i,j,k for each pair of vertices i, j representing whether the edge between them is given in graph k g i,j,k对于每对顶点i,j表示它们之间的边是否在图k中给出
  • s i,j for each pair of vertices i, j representing whether adding an edge between them is part of the solution s i,j对于每对顶点i,j表示在它们之间添加一条边是否是解的一部分

and assertions:和断言:

  • g i,j,k if the edge ( i, j ) is given in graph k g i,j,k如果边 ( i, j ) 在图k中给出
  • ¬g i,j,k if the edge ( i, j ) is not given in graph k ¬g i,j,k如果在图k没有给出边 ( i, j )
  • c i,j,k if the graph k requires i and j to be connected c i,j,k如果图k需要ij连接
  • ¬s i,j ∧ ¬c i,j,k if the graph k requires i and j to not be connected ¬s i,j ∧ ¬c i,j,k如果图k要求ij连通
  • s i,j ⇒ c i,j,k for all graphs k s i,j ⇒ c i,j,k对于所有图k
  • c i,j,k = s i,j ∨ g i,j,k ∨ ((s i,z ∨ g i,z,k ) ∧ c z,j,k ) for all vertices i, j, z and graphs k c i,j,k = s i,j ∨ g i,j,k ∨ ((s i,z ∨ g i,z,k ) ∧ c z,j,k ) 对于所有顶点i, j, z和图k

The last clause, however, does not seem to work as intended for expressing connectivity and z3 is returning sat but the clearly necessary s i,j are false in the resulting model.然而,最后一个子句似乎没有按预期工作以表达连接性,并且 z3 正在返回sat ,但在结果模型中显然必要的 s i,j是错误的。

Here's the shortest I could manage to make a minimal example of the problem:这是我能做到的最短的问题示例:

from itertools import combinations

from z3 import *

vertices = [0, 1, 2, 3, 4]
edges = [tuple(sorted(edge)) for edge in list(combinations(vertices, 2))]

graphs = {
    "G": [(0, 1)],
    "H": [(1, 3)],
}

facts = Solver()

connected_in_graph = {}
for graph in graphs:
    for edge in edges:
        connected_in_graph[(graph, edge)] = Bool(f"{edge}_conn_in_{graph}")

solution_edges = {}
graph_given_edges = {}
for edge in edges:
    edge_in_solution = Bool(f"{edge}_in_solution")
    solution_edges[edge] = edge_in_solution
    for graph in graphs:
        given = Bool(f"{graph}_{edge}_given")
        graph_given_edges[(graph, edge)] = given
        if edge in graphs[graph]:
            facts.append(given)
        else:
            facts.append(Not(given))

facts.append(
    connected_in_graph[("G", (0, 2))],
    connected_in_graph[("H", (2, 4))],
    Not(connected_in_graph[("H", (0, 2))]),
)

for edge in edges:
    for graph in graphs:
        ors = [
            solution_edges[edge],
            graph_given_edges[(graph, edge)],
        ]
        for vertex in vertices:
            if vertex in edge:
                continue
            l_edge = tuple(sorted((edge[0], vertex)))
            r_edge = tuple(sorted((edge[1], vertex)))
            ors.append(
                And(
                    Or(
                        solution_edges[l_edge],
                        graph_given_edges[(graph, l_edge)],
                    ),
                    connected_in_graph[(graph, r_edge)],
                )
            )
        facts.append(connected_in_graph[(graph, edge)] == Or(*ors))

print(facts.check())
model = facts.model()
for edge in edges:
    c = solution_edges[edge]
    if model[c]:
        print(c)

I suppose what I'd actually need to express, rather than that last constraint, is relations:我想我实际上需要表达的,而不是最后一个约束,是关系:

c2( i , j , k , through ) = s i,j ∨ g i,j,k ∨ (∃z. z ∉ (ignored ∪ {i, j}) ∧ (s i,z ∨ g i,z,k ) ∧ c(z, j, k, ignored ∪ {i})) c2( i , j , k , through ) = s i,j ∨ g i,j,k ∨ (∃z.z ∉ (忽略 ∪ {i, j}) ∧ (s i,z ∨ g i,z, k ) ∧ c(z, j, k, 忽略 ∪ {i}))

c( i , j , k ) = c2( i , j , k , {}) c( i , j , k ) = c2( i , j , k , {})

However, reducing that to just unquantified boolean terms would obviously take somewhere on the order of M!但是,将其简化为未量化的布尔项显然需要 M 的数量级! time and space.时间和空间。

Is there a better way of expressing the existence of a path in a graph in SAT/SMT, or perhaps a better way of solving this problem altogether?有没有更好的方法来表达 SAT/SMT 图形中路径的存在,或者完全解决这个问题的更好方法?


Alias's suggestion to use transitive closure seems to be the solution to this, but I seem to have trouble using it properly. Alias 建议使用传递闭包似乎是解决此问题的方法,但我似乎无法正确使用它。 My revised code:我修改后的代码:

from itertools import combinations

from z3 import *

vertices = [0, 1, 2, 3, 4]
edges = [tuple(sorted(edge)) for edge in list(combinations(vertices, 2))]

graphs = {
    "G": [(0, 1)],
    "H": [(1, 3)],
}

facts = Solver()

vs = {}
VertexSort = DeclareSort("VertexSort")
for vertex in vertices:
    vs[vertex] = Const(f"V{vertex}", VertexSort)
facts.add(Distinct(*vs.values()))

given = {}
directly_connected = {}
tc_connected = {}
for graph in graphs:
    given[graph] = Function(
        f"{graph}_given", VertexSort, VertexSort, BoolSort()
    )
    directly_connected[graph] = Function(
        f"directly_connected_{graph}", VertexSort, VertexSort, BoolSort()
    )
    tc_connected[graph] = TransitiveClosure(directly_connected[graph])

in_solution = Function("in_solution", VertexSort, VertexSort, BoolSort())


for edge in edges:
    # commutativity
    facts.add(
        in_solution(vs[edge[0]], vs[edge[1]])
        == in_solution(vs[edge[1]], vs[edge[0]])
    )
    for graph in graphs:
        # commutativity
        facts.add(
            given[graph](vs[edge[0]], vs[edge[1]])
            == given[graph](vs[edge[1]], vs[edge[0]])
        )
        facts.add(
            directly_connected[graph](vs[edge[0]], vs[edge[1]])
            == directly_connected[graph](vs[edge[1]], vs[edge[0]])
        )
        # definition of direct connection
        facts.add(
            directly_connected[graph](vs[edge[0]], vs[edge[1]])
            == Or(
                in_solution(vs[edge[0]], vs[edge[1]]),
                given[graph](vs[edge[0]], vs[edge[1]]),
            ),
        )
        if edge in graphs[graph]:
            facts.add(given[graph](vs[edge[0]], vs[edge[1]]))
        else:
            facts.add(Not(given[graph](vs[edge[0]], vs[edge[1]])))


facts.append(
    tc_connected["G"](vs[0], vs[2]),
    tc_connected["H"](vs[2], vs[4]),
    Not(tc_connected["H"](vs[0], vs[2])),
)

print(facts.check())
model = facts.model()
print(model)
print(f"solution: {model[in_solution]}")

Prints sat but finds the definition in_solution = [else -> False] rather than the solution I'm looking for.打印sat但找到定义in_solution = [else -> False]而不是我正在寻找的解决方案。 What am I doing wrong?我究竟做错了什么?

As suggested by @alias in this comment , solving the problem was made possible by using transitive closures .正如@alias此评论中所建议的那样,通过使用传递闭包来解决问题成为可能。

By:经过:

  • defining relations given G (v1, v2) and directly_connected G (v1, v2) for each graph G and a relation in_solution(v1, v2) where v1, v2 are of an enumeration type with constructors for each vertex为每个图G定义关系给定G (v1, v2)和 direct_connected G (v1, v2)以及关系in_solution(v1, v2)其中v1, v2是枚举类型,每个顶点都有构造函数
  • asserting directly_connected G (v1, v2) = in_solution(v1, v2) ∨ given G (v1, v2) for each v1 , v2断言direct_connected G (v1, v2) = in_solution(v1, v2) ∨ 给定G (v1, v2)对于每个v1 , v2
  • declaring a transitive closure TC_connected G (v1, v2) for each graph G为每个图 G 声明一个传递闭包TC_connected G (v1, v2)
  • asserting constraints in terms of TC_connected根据TC_connected断言约束

I got the solver to return a correct solution for all of my test cases.我让求解器为我的所有测试用例返回正确的解决方案。

The revised code:修改后的代码:

from itertools import combinations

from z3 import *

vertices = [0, 1, 2, 3, 4]
edges = [tuple(sorted(edge)) for edge in list(combinations(vertices, 2))]

graphs = {
    "G": [(0, 1)],
    "H": [(1, 3)],
}

facts = Solver()


VertexSort = Datatype("VertexSort")
for vertex in vertices:
    VertexSort.declare(str(vertex))
VertexSort = VertexSort.create()

vs = {}
for vertex in vertices:
    vs[vertex] = getattr(VertexSort, str(vertex))


given = {}
directly_connected = {}
tc_connected = {}
for graph in graphs:
    given[graph] = Function(
        f"{graph}_given", VertexSort, VertexSort, BoolSort()
    )
    directly_connected[graph] = Function(
        f"directly_connected_{graph}", VertexSort, VertexSort, BoolSort()
    )
    tc_connected[graph] = TransitiveClosure(directly_connected[graph])

in_solution = Function("in_solution", VertexSort, VertexSort, BoolSort())


for edge in edges:
    # commutativity
    facts.add(
        in_solution(vs[edge[0]], vs[edge[1]])
        == in_solution(vs[edge[1]], vs[edge[0]])
    )
    for graph in graphs:
        # commutativity
        facts.add(
            given[graph](vs[edge[0]], vs[edge[1]])
            == given[graph](vs[edge[1]], vs[edge[0]])
        )
        facts.add(
            directly_connected[graph](vs[edge[0]], vs[edge[1]])
            == directly_connected[graph](vs[edge[1]], vs[edge[0]])
        )
        # definition of direct connection
        facts.add(
            directly_connected[graph](vs[edge[0]], vs[edge[1]])
            == Or(
                in_solution(vs[edge[0]], vs[edge[1]]),
                given[graph](vs[edge[0]], vs[edge[1]]),
            ),
        )
        if edge in graphs[graph]:
            facts.add(given[graph](vs[edge[0]], vs[edge[1]]))
        else:
            facts.add(Not(given[graph](vs[edge[0]], vs[edge[1]])))


facts.append(
    tc_connected["G"](vs[0], vs[2]),
    tc_connected["H"](vs[2], vs[4]),
    Not(tc_connected["H"](vs[0], vs[2])),
)

print(facts.check())
model = facts.model()
print(f"solution: {model[in_solution]}")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM