我试图在SMT-LIB中表示时间约束,以检查它们的可满足性。我正在寻找有关我所采取方向的反馈意见。我对SMT-LIB相对较新,我非常欣赏输入。
我所拥有的约束是关于事件的时间和持续时间。例如,考虑以自然语言给出的以下约束:
John于13:03:41开始写一篇文章,花了20分钟完成它。
写完之后,他检查了他的电子邮件,这花了他超过40分钟。
检查完电子邮件后,他打电话给他的妻子。
他在14:00:00之后打电话给他的妻子。
很容易看出这组约束是令人满意的,我试图用SMT求解器来推断它。
为了对时间和持续时间的概念有一些封装的痛处,我定义了在数组中表示它们的新排序。一些宏被定义为用作结构:
(define-sort Time () (Array Int Int))
(define-fun new_time_ns ((hours Int) (minutes Int) (seconds Int) (nanos Int)) Time
(store (store (store (store ((as const (Array Int Int)) 0) 1 hours) 2 minutes) 3 seconds) 4 nanos)
)
(define-sort Duration () (Array Int Int))
(define-fun new_duration_ns ((seconds Int) (nanos Int)) Duration
(store (store ((as const (Array Int Int)) 0) 1 seconds) 2 nanos)
)
使用宏定义getter并允许我们检索特定的度量,例如:
(define-fun getDurationSecond ((d Duration)) Int
(select d 1)
)
(define-fun getDurationNano ((d Duration)) Int
(select d 2)
)
为时间和持续时间算术以及表达关系定义了一些实用程序宏。例如,使用一些辅助宏我们定义 isLongerThan , isShorterThan 和 isEqualDuration 如下:
(define-fun cmpInt ((i1 Int) (i2 Int)) Int
(ite (< i1 i2) -1 (ite(> i1 i2) 1 0))
)
(define-fun cmpDuration ((d1 Duration) (d2 Duration)) Int
(ite (= (cmpInt (getDurationSecond d1) (getDurationSecond d2)) 0)
(ite (= (cmpInt (getDurationNano d1) (getDurationNano d2)) 0)
0
(cmpInt (getDurationNano d1) (getDurationNano d2)))
(cmpInt (getDurationSecond d1) (getDurationSecond d2)))
)
(define-fun isLongerThan ((d1 Duration) (d2 Duration)) Bool
(> (cmpDuration d1 d2) 0)
)
(define-fun isShorterThan ((d1 Duration) (d2 Duration)) Bool
(< (cmpDuration d1 d2) 0)
)
(define-fun isEqualDuration ((d1 Duration) (d2 Duration)) Bool
(= (cmpDuration d1 d2) 0)
)
其他定义可以在this file中找到。
基于此,我可以通过一组断言来表达约束:
(declare-const write_start Time)
(declare-const write_end Time)
(declare-const write_duration Duration)
(declare-const check_start Time)
(declare-const check_end Time)
(declare-const check_duration Duration)
(declare-const phone_start Time)
(assert (= write_start (new_time_ns 13 03 41 0))) ; the writing started at 13:03:41
(assert (= write_duration (new_duration 1200))) ; the writing took 20 min (1200 sec).
(assert (= write_end (plusDuration write_start write_duration)))
(assert (isAfter check_start write_end)) ; the check started after the writing
(assert (isLongerThan check_duration (new_duration 2400))) ; the check took more than 40 min
(assert (= check_end (plusDuration check_start check_duration)))
(assert (isAfter phone_start check_end)) ; he phoned after the check
(assert (isAfter phone_start (new_time_ns 14 00 00 0))) ; it was after 14:00:00
(check-sat)
一些问题和疑问:
在设计方面,我有兴趣知道这是否是SMT-LIB中问题的合理建模。
要添加的一些注释:(A)我决定使用数组来表示时间和持续时间对象,因为它们有助于对这些概念的内部字段(小时,分钟,秒,纳米)进行分组。也可以使用单个整数。 (B)我非常依赖宏(define-fun ...),这可能会使约束变得有点复杂,但我不知道还有什么可以用来达到所需的水平当前代码具有的表现力和清晰度。 (C)目前没有限制时间字段的约束,因此例如,分钟字段的值可以是78.应该添加断言,将秒限制为59,将分钟限制为59,将小时数限制为23 ,但我没有找到一种优雅的方式来做到这一点。
我假设我在FOL的可判定片段 - QF_LIA中 - 因为所有约束都是使用线性函数在整数常量上声明的。但是,我尝试通过Z3运行the attached code,即使在普通计算机上本地运行40分钟后,它仍然没有以分辨率(sat /不满)返回。我实际上根本不知道它是否可以解决问题。我在QF-LIA中的假设可能是错误的,并且Z3也可能在这种类型的约束下挣扎。我可以补充一点,当我尝试更简单的约束时,Z3设法达到分辨率,但我注意到它生成的模型非常复杂,有很多内部结构。有人可以给我一些想法来调查吗?我的代码可以找到Z3的在线证明here。我还没有尝试过其他的SMT求解器。
我不知道尝试在SMT-LIB中定义此类型的时间约束的并行工作。我非常感谢对现有作品的引用。
谢谢!
答案 0 :(得分:4)
我喜欢你的方法,但我认为通过定义你自己的种类,尤其是使用数组理论,你的情况过于复杂。
另外,从数学上看,整数理论比实际理论更难。例如&#34; n = pq ,求解 p &#34;如果 n , p ,并且 q 是实数,则是微不足道的,但如果它们是整数,那么它是整数因子分解,这很难。类似地, x n + y n = z n ,n> 2 在实际中很容易解决,但在整数中,这是费马的最后定理。这些例子是非线性的,但是如果你认为用于解决QF_LRA的技术被教给中学和高中学生,我声称你在QF_LRA比QF_LIA更好。无论如何,时间更接近实数而不是一堆整数。
根据我对SMT解算器的总体经验,特别是Z3,你最好使用更简单的理论。它将允许Z3使用其最强大的内部解算器。如果您使用更复杂的理论(如数组),您可能会得到一个惊人的结果,或者您可能会发现求解器超时并且您不知道为什么,就像您提出的解决方案一样。
从软件工程的角度来看,它不是很好,但在数学上我推荐以下简单的解决方案来解决你提出的问题,其中所有时间都表示为实数,以秒为单位。感兴趣的当天午夜:\n
Z3和CVC4都能快速找到解决方案:
; Output all real-valued numbers in decimal-format.
(set-option :pp.decimal true)
; Convenience function for converting hh:mm:ss formatted times to seconds since midnight.
(define-fun time_hms ((hour Real) (minute Real) (second Real)) Real
(+ (+ (* 3600.0 hour) (* 60.0 minute)) second)
)
; Convenience function for converting durations in minutes to seconds.
(define-fun duration_m ((minute Real)) Real
(* 60.0 minute)
)
; Variable declarations. Durations are in seconds. Times are in seconds since midnight.
(declare-fun write_start () Real)
(declare-fun write_end () Real)
(declare-fun write_duration () Real)
(declare-fun check_start () Real)
(declare-fun check_end () Real)
(declare-fun check_duration () Real)
(declare-fun phone_start () Real)
; Constraints.
; 1. John started writing an essay at 13:03:41, and it took him 20 min to complete it.
(assert (= write_start (time_hms 13 03 41)))
(assert (= write_duration (duration_m 20)))
(assert (= write_end (+ write_start write_duration)))
; 2. After writing, he checked his emails, which took him more than 40 min.
(assert (> check_start write_end))
(assert (> check_duration (duration_m 40)))
(assert (= check_end (+ check_start check_duration)))
; 3. After checking his emails, he phoned his wife.
(assert (> phone_start check_end))
; 4. He phoned his wife after 14:00:00.
(assert (> phone_start (time_hms 14 00 00)))
(check-sat)
(get-value (write_start write_duration write_end check_start check_end check_duration phone_start))
(exit)