讲师中心 微信公众号
AI工具推荐 视频效率加速

Python PDDL框架中旅行商问题递归深度错误及动作效果的正确定义

风丽同学_5923

风丽同学_5923

发布时间:2025-11-24 14:33:46

|

544人浏览过

|

来源于php中文网

原创

python pddl框架中旅行商问题递归深度错误及动作效果的正确定义

在使用Python PDDL框架结合Fast-Downward规划器解决复杂问题,如旅行商问题(TSP)时,开发者可能会遇到各种挑战。其中一个常见的陷阱是在定义PDDL领域中的动作效果(effect)或问题中的目标状态(goal)时,错误地使用了字符串拼接的方式,而非框架提供的逻辑对象和运算符。这种不当的操作可能导致在框架内部进行模型验证时触发“最大递归深度超出”的错误(`RecursionError: maximum recursion depth exceeded`)。本文旨在深入分析这一问题,并提供一套专业的解决方案和最佳实践。

1. PDDL框架与旅行商问题概述

pddl-python-framework是一个强大的工具,它允许开发者使用Python语言来定义PDDL领域(Domain)和问题(Problem),随后可以将其转换为标准的PDDL文件,并由外部规划器(如Fast-Downward)进行求解。旅行商问题(TSP)是一个经典的组合优化问题,其目标是找到访问一系列城市并返回起始城市的最短路径,且每个城市只能访问一次。在PDDL中建模TSP通常涉及定义城市之间的连接、当前位置、已访问城市等谓词,以及表示移动行为的动作。

2. 错误的动作效果和目标状态定义方式

在PDDL中,动作效果和目标状态都是由一系列逻辑表达式组成的。例如,一个移动动作的效果可能包括更新当前位置、标记目标城市为已访问,并移除旧位置的标记。同样,目标状态可能要求所有城市都被访问,并且规划器最终位于起始城市。

当开发者尝试通过字符串拼接来构建这些逻辑表达式时,问题就出现了。考虑以下在TSPBasic类中定义move动作效果的错误示例:

立即学习“Python免费学习笔记(深入)”;

# tsp_basic.py (错误示例)

class TSPBasic:
    # ... (省略其他初始化代码)

    def create_actions(self):
        move = Action(
            "move",
            parameters=[self.start, self.finish],
            precondition=self.at(self.start) &
                         self.connected(self.start, self.finish) &
                         ~self.visited(self.finish),
            effect="&".join([  # 错误:使用字符串拼接
                str(self.at(self.finish)),
                str(self.visited(self.finish)),
                "~" + str(self.at(self.start))
            ])
        )
        return [move]

    def create_problem(self):
        # ... (省略其他代码)
        goal_conditions = [self.visited(city) for city in self.cities if city.name != self.start_city]
        goal_conditions.append(self.at(variables(self.start_city, types=["position"])[0]))
        goal = "&".join(goal_conditions) # 错误:使用字符串拼接
        # ... (省略其他代码)

在这个错误示例中,effect和goal被构建为单个字符串,而不是由pddl.logic模块提供的谓词对象和逻辑运算符组合而成的复杂逻辑表达式。当pddl-python-framework尝试验证或解析这些字符串时,它无法将其识别为内部的PDDL逻辑结构,这可能导致其内部的类型检查或AST(抽象语法树)构建逻辑陷入无限递归,最终触发RecursionError。

python-parallelization
python-parallelization

将顺序 Python 代码转换为并行/并发实现,用于在需要并行化 Python 代码、提升性能时使用。

下载

3. RecursionError的根本原因

pddl-python-framework在构建PDDL领域和问题时,会对所定义的谓词、动作、效果、目标等进行严格的类型和结构验证。当effect或goal被定义为一个简单的字符串时,框架会尝试将其解析成预期的PDDL逻辑表达式对象。由于字符串本身不具备框架所期望的内部结构,验证器可能会在尝试递归地解析或转换这个非标准对象时,耗尽Python的默认递归深度限制。

4. 正确的动作效果和目标状态定义方式

解决此问题的关键在于,始终使用pddl.logic模块提供的谓词对象和逻辑运算符(如&表示逻辑AND,|表示逻辑OR,~表示逻辑NOT)来构建PDDL的逻辑表达式。这些运算符已被框架重载,可以直接作用于谓词对象,从而正确地构建PDDL的抽象语法树。

以下是TSPBasic类中move动作效果和goal状态的正确定义方式:

# tsp_basic.py (正确示例)

from pddl.logic import Predicate, variables, And, Not # 导入And和Not

class TSPBasic:
    def __init__(self, connections, start_city):
        self.connections = connections
        self.start_city = start_city

        unique_cities = set()
        for start, end in connections:
            unique_cities.update([start, end])
        # 将城市名称转换为PDDL变量对象,并将其类型设置为"position"
        self.cities = [variables(city_name, types=["position"])[0] for city_name in unique_cities]

        self.start_var = variables("start_var", types=["position"])[0] # 定义用于动作参数的变量
        self.finish_var = variables("finish_var", types=["position"])[0] # 定义用于动作参数的变量

        # Predicates
        self.at = Predicate("at", self.start_var) # 谓词定义应使用变量
        self.connected = Predicate("connected", self.start_var, self.finish_var)
        self.visited = Predicate("visited", self.finish_var)

        self.domain = self.create_domain()
        self.problem = self.create_problem()

    def create_actions(self):
        # 注意:Action的parameters应使用未绑定的变量
        move = Action(
            "move",
            parameters=[self.start_var, self.finish_var], # 使用未绑定的变量
            precondition=self.at(self.start_var) &
                         self.connected(self.start_var, self.finish_var) &
                         ~self.visited(self.finish_var),
            effect=self.at(self.finish_var) & # 正确:直接使用逻辑运算符
                   self.visited(self.finish_var) &
                   ~self.at(self.start_var)
        )
        return [move]

    def create_problem(self):
        requirements = [Requirements.STRIPS, Requirements.TYPING]

        # 定义objects,应为具体的常量对象,而不是变量
        # 这里需要将字符串城市名转换为PDDL常量对象
        pddl_city_objects = [variables(city.name, types=["position"])[0] for city in self.cities]

        # 初始状态
        # 将start_city转换为PDDL常量对象
        start_city_obj = variables(self.start_city, types=["position"])[0]
        init = [self.at(start_city_obj)]
        for start, finish in self.connections:
            init.append(self.connected(variables(start, types=["position"])[0],
                                       variables(finish, types=["position"])[0]))

        # 目标状态:所有城市必须被访问,并返回到起始城市
        goal_conditions = [self.visited(city) for city in pddl_city_objects if city.name != self.start_city]
        goal_conditions.append(self.at(start_city_obj))
        goal = And(*goal_conditions) # 正确:使用And函数组合逻辑表达式

        problem = Problem(
            "tsp_basic_problem",
            domain=self.domain,
            requirements=requirements,
            objects=pddl_city_objects, # 确保这里传递的是常量对象
            init=init,
            goal=goal
        )
        return problem

    # ... (get_domain 和 get_problem 方法不变)

关键修改点解释:

  1. 动作效果(effect): 将"&".join(...)替换为直接使用&运算符连接谓词表达式。例如,self.at(self.finish_var) & self.visited(self.finish_var) & ~self.at(self.start_var)。pddl.logic中的谓词对象重载了&和~等运算符,使其能够构建正确的逻辑结构。
  2. 目标状态(goal): 同样,将"&".join(goal_conditions)替换为And(*goal_conditions)。pddl.logic.And函数可以接受多个逻辑表达式作为参数,并将其组合成一个大的合取(AND)表达式。
  3. 变量与常量: 在__init__中,self.cities应是一个包含PDDL变量对象的列表。在create_problem中,objects列表应包含代表具体城市实体的PDDL常量对象。init和goal中引用的城市也应是这些常量对象。为了清晰,将动作参数变量与实际的城市常量区分开,例如使用self.start_var和self.finish_var作为动作的参数,而variables("city_name", ...)用于创建实际的城市常量。

5. 注意事项与最佳实践

  • 避免手动字符串拼接: 永远不要尝试通过手动拼接字符串来构建PDDL的逻辑表达式(如precondition, effect, goal)。这会绕过框架的内部机制,导致难以调试的错误。
  • 利用框架提供的API: 熟悉并充分利用pddl.logic模块提供的类和函数,如Predicate, variables, constants, And, Or, Not等。它们是构建有效PDDL模型的基石。
  • 理解变量与常量: 在PDDL中,变量(如动作参数)和常量(如具体的城市实例)是不同的概念。确保在定义动作时使用变量,在定义问题实例时使用常量。
  • 类型一致性: 确保所有谓词、变量和常量的类型定义一致,例如所有城市都属于position类型。
  • 逐步测试: 在构建复杂的PDDL模型时,建议分阶段测试。例如,先确保领域定义(包括谓词和动作)能够成功生成,再逐步添加问题定义和目标状态。

6. 总结

在使用pddl-python-framework进行AI规划开发时,正确地构建PDDL的逻辑表达式至关重要。通过避免字符串拼接,并充分利用框架提供的逻辑运算符和辅助函数,开发者可以有效地避免RecursionError等常见问题,从而构建出健壮、可解析的PDDL领域和问题模型,并成功地与Fast-Downward等规划器集成,解决复杂的规划任务。理解并遵循这些最佳实践,将大大提高开发效率和规划系统的可靠性。

热门AI工具

更多
Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

Lovart
Lovart Hot

一款面向视觉设计创作的AI设计平台,可通过智能体和画布工作流辅助制作海报、Logo、网页、PPT及其他视觉内容。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

相关专题

更多
java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

6064

2023.10.24

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

6064

2023.10.24

Go语言中的运算符有哪些
Go语言中的运算符有哪些

Go语言中的运算符有:1、加法运算符;2、减法运算符;3、乘法运算符;4、除法运算符;5、取余运算符;6、比较运算符;7、位运算符;8、按位与运算符;9、按位或运算符;10、按位异或运算符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2924

2024.02.23

php三元运算符用法
php三元运算符用法

本专题整合了php三元运算符相关教程,阅读专题下面的文章了解更多详细内容。

1772

2025.10.17

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

1738

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

2684

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

6064

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

5649

2023.11.24

OpenJDK JVM参数怎么调优
OpenJDK JVM参数怎么调优

Java应用运行缓慢,如何调整JVM参数?本专题介绍OpenJDK JVM性能调优的基本流程,涵盖性能指标确认、运行数据采集、内存配置和垃圾回收分析,帮助你先定位瓶颈,再通过对照测试验证效果,避免直接套用参数而影响应用稳定性。

60

2026.10.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Uvicorn官方安装与入门手册
Uvicorn官方安装与入门手册

共0课时 | 0人学习

Python uv官方项目管理指南
Python uv官方项目管理指南

共0课时 | 0人学习

Python uv官方安装与入门文档
Python uv官方安装与入门文档

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn