使用 z3 库的 Python

问题描述 投票:0回答:1

我需要找到从初始位置穿过冰冻湖到目的地的所有可能路径。结冰的湖泊由一个矩阵表示,其中每个单元格由 1(安全单元格)或 0(不安全单元格)组成。我想使用 Z3 定理证明器编写代码来找到从初始位置到目的地的有效路径,而不使用任何不安全的单元格。

这是一个 e# 用法示例

在此矩阵中,单元格 D1 是起始位置,C3 是目标位置。单元格 A1、A2、A3、B1、B3、C1、C3 和 D1 是安全单元格(以橙色标记),而单元格 A4、B2、B4、C2、C4、D2、D3 和 D4 是不安全单元格(以橙色标记)红色的)。从初始位置到目标位置只有一条有效路径,由 D1 -> C1 -> B1 -> A1 -> A2 -> A3 -> B3 -> C3 给出。

为了解决这个问题,我使用Python中的Z3定理证明器。这是代码:

from z3 import *

def find_path(matrix, start, end):
    # Define the dimensions of the matrix
    rows = len(matrix)
    cols = len(matrix[0])

    # Create Z3 variables for each cell in the matrix
    cells = [[Bool(f'cell_{i}_{j}') for j in range(cols)] for i in range(rows)]

    # Create a Z3 solver
    s = Solver()

    # Add constraints for the starting and destination positions
    s.add(cells[start[0]][start[1]])  # Starting position
    s.add(cells[end[0]][end[1]])  # Destination position

    # Add constraints for safe and unsafe cells
    for i in range(rows):
        for j in range(cols):
            if matrix[i][j] == 0:
                s.add(Not(cells[i][j]))  # Unsafe cell
            else:
                s.add(cells[i][j])  # Safe cell

    # Add constraints for adjacent cells
    for i in range(rows):
        for j in range(cols):
            adjacent_cells = []
            if i > 0:
                adjacent_cells.append(cells[i-1][j])  # Cell above
            if i < rows - 1:
                adjacent_cells.append(cells[i+1][j])  # Cell below
            if j > 0:
                adjacent_cells.append(cells[i][j-1])  # Cell to the left
            if j < cols - 1:
                adjacent_cells.append(cells[i][j+1])  # Cell to the right
            s.add(Implies(cells[i][j], Or(adjacent_cells)))  # Only connect to adjacent safe cells
    
    print(s)
    # Check if there is a valid path from the starting position to the destination
    if s.check() == sat:
        model = s.model()
        path = []
        for i in range(rows):
            for j in range(cols):
                if model[cells[i][j]]:
                    path.append((i, j))
        s.reset()
        return path
    else:
        return None

# Example usage
matrix = [[1, 1, 1, 0],
          [1, 0, 1, 0],
          [1, 0, 1, 0],
          [1, 0, 0, 0]]
start = (3, 0)
end = (2, 2)

path = find_path(matrix, start, end)
if path:
    print("Valid path found:")
    for cell in path:
        print(f"({chr(ord('A') + cell[0])}{cell[1] + 1})")
else:
    print("No valid path found.")

在此代码中,我首先定义矩阵的维度并为每个单元格创建 Z3 变量。然后,我添加对起始位置和目的地位置以及安全和不安全单元格的约束。最后,我添加了对相邻单元格的约束,以确保路径仅连接到相邻的安全单元格。我使用 Z3 解算器检查从起始位置到目的地是否存在有效路径,如果是,则检索该路径。

但是,当我运行此代码时,我没有得到预期的输出。约束条件或我对问题建模的方式似乎存在问题。

到底出了什么问题,如何解决?

python z3 z3py
1个回答
1
投票

您不需要为矩阵本身创建符号值:它是一个常量数组。相反,创建与路径相对应的变量,并告诉 z3 这应该是对该矩阵的有效遍历。然后 z3 会找到路径变量的赋值,为您解决迷宫问题。比如:

from z3 import *

def find_path(matrix, start, end):

    # Define the dimensions of the matrix
    rows = len(matrix)
    cols = len(matrix[0])

    # symbolic look-up into the matrix:
    def lookup(x, y):
        val = 0
        for r in range(rows):
            for c in range(cols):
                val = If(And(x == r, y == c), matrix[r][c], val)
        return val

    # Create a path, there are at most rows*cols elements
    path = []
    for r in range(rows):
        for c in range(cols):
            path.append([FreshInt(), FreshInt()])

    s = Solver()

    # assert that the very first element of the path is the start position:
    s.add(path[0][0] == start[0])
    s.add(path[0][1] == start[1])

    # for each remaining path-element, make sure either we reached the end, or it's a valid move
    prev = path[0]
    done = False
    for p in path[1:]:
        valid1 = And(p[0] >= 0, p[0] < rows, p[1] >= 0, p[1] < cols)  # Valid coords

        valid2 = Or( And(p[0] == prev[0]-1, p[1] == prev[1])     #    Go up
                   , And(p[0] == prev[0]+1, p[1] == prev[1])     # or Go down
                   , And(p[0] == prev[0],   p[1] == prev[1]+1)   # or Go right
                   , And(p[0] == prev[0],   p[1] == prev[1]-1))  # or Go left

        valid3 = lookup(p[0], p[1]) == 1 # The cell is safe

        # Either we're done, or all valid conditions must hold
        s.add(Or(done, And(valid1, valid2, valid3)))

        prev = p

        # We're done if p is the end position:
        done = Or(done, And(p[0] == end[0], p[1] == end[1]))

    # Make sure the path is unique:
    for i in range(len(path)):
        for j in range(len(path)):
            if j <= i:
                continue
            s.add(Or(path[i][0] != path[j][0], path[i][1] != path[j][1]))

    # Compute the path:
    if s.check() == sat:
        model = s.model()
        walk = []
        for p in path:
            cur = [model[p[0]].as_long(), model[p[1]].as_long()]
            walk.append(cur)
            if (cur[0] == end[0] and cur[1] == end[1]):
                break
        return walk
    else:
        return None

# Example usage
matrix = [[1, 1, 1, 0],
          [1, 0, 1, 0],
          [1, 0, 1, 0],
          [1, 0, 0, 0]]
start = (3, 0)
end = (2, 2)

path = find_path(matrix, start, end)
if path:
    print("Valid path found:")
    for cell in path:
        print(f"({chr(ord('A') + cell[0])}{cell[1] + 1})")
else:
    print("No valid path found.")

运行时,将打印:

Valid path found:
(D1)
(C1)
(B1)
(A1)
(A2)
(A3)
(B3)
(C3)

这就是您正在寻找的解决方案。

© www.soinside.com 2019 - 2024. All rights reserved.