You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PySMT结合Z3求解器:如何均匀随机抽取公式的n个解?

如何用PySMT+Z3实现约束解的近似均匀采样

问题描述

我需要从给定的SMT公式中抽取n个解,采用PySMT结合Z3求解器实现。当前做法是每次调用get_model得到一个解后,将该解的否定加入公式迭代求解,但最终变量的取值分布很差,而均匀分布对我的项目至关重要。有没有办法让抽取的n个样本中,所有变量的取值在其值域内近似均匀分布?

当前代码如下:

solutions = []

for s in range(n_samples):
        solution = []
        model = get_model(formula)

       if model is None:
            print("Not enough solutions")
            break

        sample = []
        for o in self.objects:
            attributes = {
                attr: [
                    x
                    for x in self.values[attr]
                    if model[self.get_attribute(attr, o, x)].is_true()
                ][0]
                for attr in self.values
            }
            sample.append(attributes)

        solutions.append(sample)

        # Exclude the current model by adding a negation of its assertions
        # to the problem, so the next iteration finds a different solution.
        negation = Not(And(EqualsOrIff(x, y) for (x, y) in model))
        formula = And(formula, negation)

核心问题分析

你当前的方法本质是增量排除已得解,Z3的默认搜索策略会优先探索相邻的解空间,导致后续解和已得解高度相似,自然出现分布不均的问题。要实现均匀采样,需要主动引导求解器遍历不同的取值区域,而不是被动排除。

可行的解决方法

1. 随机偏好约束引导

每次求解前,给每个变量添加临时随机偏好约束,引导Z3优先选择当前采样较少的取值。具体步骤:

  • 维护每个变量各取值的采样计数
  • 每次迭代时,对每个变量,计算取值的采样频率,给频率低的取值赋予更高的“偏好权重”
  • 将这些偏好作为约束加入公式,迭代后再基于原始公式重新构建约束,避免公式过度膨胀

示例修改代码:

from pysmt.shortcuts import get_model, And, Not, EqualsOrIff
import random

# 初始化采样计数:{变量: {取值: 计数}}
sample_counts = {attr: {v: 0 for v in self.values[attr]} for attr in self.values}
solutions = []
original_formula = formula  # 保存原始公式,避免叠加否定导致公式膨胀

for s in range(n_samples):
    # 构建偏好约束:优先选采样少的取值
    preference_constraints = []
    for attr in self.values:
        # 按采样计数升序排序取值,随机选低采样值作为偏好
        sorted_vals = sorted(self.values[attr], key=lambda v: sample_counts[attr][v])
        preferred_vals = random.sample(sorted_vals[:len(sorted_vals)//2], 1)
        for o in self.objects:
            attr_sym = self.get_attribute(attr, o, preferred_vals[0])
            preference_constraints.append(attr_sym)
    
    # 组合原始公式+偏好约束+排除已得解的约束
    current_formula = And(original_formula, *preference_constraints)
    if solutions:
        exclude_clauses = []
        for sample in solutions:
            clause = []
            for o_attrs in sample:
                for attr, val in o_attrs.items():
                    sym = self.get_attribute(attr, o, val)
                    clause.append(sym)
            exclude_clauses.append(Not(And(*clause)))
        current_formula = And(current_formula, *exclude_clauses)
    
    model = get_model(current_formula)
    if model is None:
        print("Not enough solutions")
        break
    
    # 解析样本并更新采样计数
    sample = []
    for o in self.objects:
        attributes = {}
        for attr in self.values:
            val = [x for x in self.values[attr] if model[self.get_attribute(attr, o, x)].is_true()][0]
            attributes[attr] = val
            sample_counts[attr][val] += 1
        sample.append(attributes)
    solutions.append(sample)

2. 分层预采样

如果变量值域不大,可以先按变量取值拆分公式,再按比例从每个子公式中抽取解:

  • 对每个变量的每个取值,生成公式 ∧ (变量=取值)的子公式
  • 统计每个子公式的解数量,按均匀分布的目标分配每个子公式的采样数
  • 从每个子公式中抽取对应数量的解(可复用你原来的迭代排除法)

这种方法能保证每个变量的取值被覆盖到,适合值域规模较小的场景。

3. 调整Z3求解器的随机策略

Z3支持随机化搜索策略,通过设置参数让求解器每次选择不同的分支,避免陷入局部解空间:

  • 在调用get_model前,配置Z3的随机种子、随机决策等参数

示例代码:

from pysmt.solvers.z3 import Z3Solver

def get_random_model(formula):
    with Z3Solver() as solver:
        solver.add_assertion(formula)
        # 设置Z3随机化参数
        solver.z3.set("random_seed", random.randint(0, 100000))
        solver.z3.set("smt.random_seed", random.randint(0, 100000))
        solver.z3.set("sat.random_initial_value", True)
        solver.z3.set("sat.random_polarity", True)
        if solver.solve():
            return solver.get_model()
        else:
            return None

# 循环中用get_random_model替代原get_model

注意事项

  • 避免公式过度膨胀:直接叠加所有已得解的否定会让公式越来越大,求解速度骤降,建议基于原始公式重新构建约束,或定期清理旧的排除条件。
  • 提前检测解空间大小:如果总解数远小于需要的采样数,均匀分布无从谈起,需提前判断并调整采样目标。

内容的提问来源于stack exchange,提问作者ravifrancesco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 16:47:04