如何将这些附加限制添加到我的零售 product/shopping Z3 规范中?
How do I add these additional constraints to my retail product/shopping Z3 spec?
所以我创建了一个基本的 Z3 规格来指导我购买一些特定的零售产品。到目前为止,它所能做的就是在不超过我可用资金的情况下任意告诉我买什么:
(declare-datatypes () ((Retailer FooMart BazTrading)))
(declare-datatypes () ((Cartridge .223Rem .7.62x39mm)))
(declare-datatypes () ((Casing Brass Steel)))
(declare-datatypes () ((Offer (Offer
(getRetailer Retailer)
(getCartridge Cartridge)
(getRounds Int) ; # of rounds
(getPrice Int) ; price in cents
(getCasing Casing)
(getQuantityAvail Int)
))))
(declare-const x1 Offer)
(declare-const x2 Offer)
(declare-const x3 Offer)
(declare-const x4 Offer)
(declare-const x5 Offer)
(assert (= x1 (Offer FooMart .223Rem 1000 17000 Steel 50)))
(assert (= x2 (Offer BazTrading .223Rem 500 13000 Brass 10)))
(assert (= x3 (Offer FooMart .7.62x39mm 1000 18000 Steel 15)))
(assert (= x4 (Offer BazTrading .7.62x39mm 100 1850 Steel 20)))
(assert (= x5 (Offer BazTrading .7.62x39mm 20 190 Steel 20)))
; the quantity purchased of each offer/product will be
; between 0 and the max quantity of the offer
(declare-const x1Qty Int)
(assert (>= x1Qty 0))
(assert (<= x1Qty (getQuantityAvail x1)))
(declare-const x2Qty Int)
(assert (>= x2Qty 0))
(assert (<= x2Qty (getQuantityAvail x2)))
(declare-const x3Qty Int)
(assert (>= x3Qty 0))
(assert (<= x3Qty (getQuantityAvail x3)))
(declare-const x4Qty Int)
(assert (>= x4Qty 0))
(assert (<= x4Qty (getQuantityAvail x4)))
(declare-const x5Qty Int)
(assert (>= x5Qty 0))
(assert (<= x5Qty (getQuantityAvail x5)))
; let's say i've got 0 to spend
(declare-const moneyToSpend Int)
(assert (= moneyToSpend 50000))
(declare-const amountSpent Int)
(assert (= amountSpent
(+ (* x1Qty (getPrice x1))
(* x2Qty (getPrice x2))
(* x3Qty (getPrice x3))
(* x4Qty (getPrice x4))
(* x5Qty (getPrice x5)))
))
(assert (< amountSpent moneyToSpend))
(maximize amountSpent)
(check-sat)
(get-model)
此时我想做的是在我只想购买 Brass
盒装墨盒时添加额外的断言,或者如果我只需要特定类型的墨盒,如 .223Rem
,或为每个 Retailer
添加运费计算加上带有 minimize
约束的 $/round 计算,这样我就不会在运费上浪费金钱,或考虑到某些优惠的批量定价折扣(例如购买 5 个或更多,每件节省 0.10 美元,购买 10 件或更多,每件节省 0.15 美元。
我遇到的问题是,我觉得我为这些问题提出的解决方案是重复的,而且不是很优雅,我觉得我遗漏了什么……特别是因为我好像带着一个很多开销我自己跟踪每个 xQty
和它对应的 Offer
之间的关系。
例如,仅过滤 Brass
大小写的解决方案可能是这样的:
; non-Brass casings get set to qty 0
(assert (if (= (getCasing x1) Brass)
(>= x1Qty 0)
(= x1Qty 0)))
(assert (if (= (getCasing x2) Brass)
(>= x2Qty 0)
(= x2Qty 0)))
; ... etc.
也许更好的例子是尝试实施批量折扣。假设我想获取报价 x1
:
的批量折扣
; at this point i'm hardcoding prices in a bag-on-the-side
; function instead of in the x1 Offer itself :/
(define-fun x1Pricing ((qty Int)) Int
(if (>= 10 qty)
(* qty 16500)
(if (>= 5 qty)
(* qty 16800)
(* qty (getPrice x1))))
)
(declare-const amountSpent Int)
(assert (= amountSpent
(+ (x1Pricing x1Qty)
(* x2Qty (getPrice x2))
(* x3Qty (getPrice x3))
(* x4Qty (getPrice x4))
(* x5Qty (getPrice x5)))
))
(assert (< amountSpent moneyToSpend))
感觉事情非常不同,我想知道是否需要。比如有没有办法让 x1Pricing
成为 Offer
数据类型的一部分,如果是这样,那是不是一个好方法?
我的直觉也是将事物放入列表中并在它们之上进行映射等等;但是当我看到 z3 有一个本地 List 实现时,我无法理解如何使用它或者它是否合适(使用 select
和特定索引号编写 assert
s 似乎与完全单独的常量)。
所以我的问题是,是否有更优化的方式来完成我想做的事情?
从一些 Z3Py 示例中我看到我似乎可以通过使用 Python 列表理解和基于索引关联事物来减少一些重复,但我也想知道我是否错过了大局这里是我要去的方向。谢谢。
编辑:我添加了一个非常dumb/naive的运费计算方法和每轮成本计算:
(declare-const shippingCost Int)
(assert (= shippingCost
; FooMart charges a flat shipping fee
(+ (if (or (= (getRetailer x1) FooMart)
(= (getRetailer x2) FooMart)
(= (getRetailer x3) FooMart)
(= (getRetailer x4) FooMart)
(= (getRetailer x5) FooMart))
7500
0
)
; BazTrading charges a flat + per 100 rounds
(if (or (= (getRetailer x1) BazTrading)
(= (getRetailer x2) BazTrading)
(= (getRetailer x3) BazTrading)
(= (getRetailer x4) BazTrading)
(= (getRetailer x5) BazTrading))
2000
0
)
(* (/ (+ (if (= (getRetailer x1) BazTrading)
(getRounds x1)
0)
(if (= (getRetailer x2) BazTrading)
(getRounds x2)
0)
(if (= (getRetailer x3) BazTrading)
(getRounds x3)
0)
(if (= (getRetailer x4) BazTrading)
(getRounds x4)
0)
(if (= (getRetailer x5) BazTrading)
(getRounds x5)
0))
100)
500)
)
))
(declare-const amountSpent Int)
(assert (= amountSpent
(+ (x1Pricing x1Qty)
(* x2Qty (getPrice x2))
(* x3Qty (getPrice x3))
(* x4Qty (getPrice x4))
(* x5Qty (getPrice x5))
shippingCost)))
(assert (< amountSpent moneyToSpend))
(declare-const totalRounds Int)
(assert (= totalRounds
(+ (* x1Qty (getRounds x1))
(* x2Qty (getRounds x2))
(* x3Qty (getRounds x3))
(* x4Qty (getRounds x4))
(* x5Qty (getRounds x5)))
))
(declare-const amountSpentPerRound Real)
(assert (= amountSpentPerRound
(div amountSpent totalRounds)
))
(assert (> amountSpent 20000))
(assert (< amountSpent 30000))
(minimize amountSpentPerRound)
(请注意,该模型在 rise4fun 在线编辑器中无法满足,但在我使用最新 Z3 源的本地机器上运行良好)
现在我真的认为我做错了什么,因为 shippingCost
总是 12600。当我 fiddle 和 amountSpent
最大和最小值断言试图将它推入仅使用 BazTrading 或 FooMart Offers(以降低运费),它会在我杀死它之前挂起几分钟。
SMTLib 不适合编写此类规范。它应该更多地被视为一种 "assembly" 语言,并且应该从其他工具生成。这就是高级 API 发挥作用的地方,具体取决于您选择的语言。有来自 C、C++、Java、Python、Haskell、Scala 的高级绑定。您基本上应该使用那些 APIs 来生成约束并使用 z3通过 API。每个绑定都有自己的 advantages/disadvantages,因此我建议从您最熟悉的宿主语言开始并使用它。
所以我创建了一个基本的 Z3 规格来指导我购买一些特定的零售产品。到目前为止,它所能做的就是在不超过我可用资金的情况下任意告诉我买什么:
(declare-datatypes () ((Retailer FooMart BazTrading)))
(declare-datatypes () ((Cartridge .223Rem .7.62x39mm)))
(declare-datatypes () ((Casing Brass Steel)))
(declare-datatypes () ((Offer (Offer
(getRetailer Retailer)
(getCartridge Cartridge)
(getRounds Int) ; # of rounds
(getPrice Int) ; price in cents
(getCasing Casing)
(getQuantityAvail Int)
))))
(declare-const x1 Offer)
(declare-const x2 Offer)
(declare-const x3 Offer)
(declare-const x4 Offer)
(declare-const x5 Offer)
(assert (= x1 (Offer FooMart .223Rem 1000 17000 Steel 50)))
(assert (= x2 (Offer BazTrading .223Rem 500 13000 Brass 10)))
(assert (= x3 (Offer FooMart .7.62x39mm 1000 18000 Steel 15)))
(assert (= x4 (Offer BazTrading .7.62x39mm 100 1850 Steel 20)))
(assert (= x5 (Offer BazTrading .7.62x39mm 20 190 Steel 20)))
; the quantity purchased of each offer/product will be
; between 0 and the max quantity of the offer
(declare-const x1Qty Int)
(assert (>= x1Qty 0))
(assert (<= x1Qty (getQuantityAvail x1)))
(declare-const x2Qty Int)
(assert (>= x2Qty 0))
(assert (<= x2Qty (getQuantityAvail x2)))
(declare-const x3Qty Int)
(assert (>= x3Qty 0))
(assert (<= x3Qty (getQuantityAvail x3)))
(declare-const x4Qty Int)
(assert (>= x4Qty 0))
(assert (<= x4Qty (getQuantityAvail x4)))
(declare-const x5Qty Int)
(assert (>= x5Qty 0))
(assert (<= x5Qty (getQuantityAvail x5)))
; let's say i've got 0 to spend
(declare-const moneyToSpend Int)
(assert (= moneyToSpend 50000))
(declare-const amountSpent Int)
(assert (= amountSpent
(+ (* x1Qty (getPrice x1))
(* x2Qty (getPrice x2))
(* x3Qty (getPrice x3))
(* x4Qty (getPrice x4))
(* x5Qty (getPrice x5)))
))
(assert (< amountSpent moneyToSpend))
(maximize amountSpent)
(check-sat)
(get-model)
此时我想做的是在我只想购买 Brass
盒装墨盒时添加额外的断言,或者如果我只需要特定类型的墨盒,如 .223Rem
,或为每个 Retailer
添加运费计算加上带有 minimize
约束的 $/round 计算,这样我就不会在运费上浪费金钱,或考虑到某些优惠的批量定价折扣(例如购买 5 个或更多,每件节省 0.10 美元,购买 10 件或更多,每件节省 0.15 美元。
我遇到的问题是,我觉得我为这些问题提出的解决方案是重复的,而且不是很优雅,我觉得我遗漏了什么……特别是因为我好像带着一个很多开销我自己跟踪每个 xQty
和它对应的 Offer
之间的关系。
例如,仅过滤 Brass
大小写的解决方案可能是这样的:
; non-Brass casings get set to qty 0
(assert (if (= (getCasing x1) Brass)
(>= x1Qty 0)
(= x1Qty 0)))
(assert (if (= (getCasing x2) Brass)
(>= x2Qty 0)
(= x2Qty 0)))
; ... etc.
也许更好的例子是尝试实施批量折扣。假设我想获取报价 x1
:
; at this point i'm hardcoding prices in a bag-on-the-side
; function instead of in the x1 Offer itself :/
(define-fun x1Pricing ((qty Int)) Int
(if (>= 10 qty)
(* qty 16500)
(if (>= 5 qty)
(* qty 16800)
(* qty (getPrice x1))))
)
(declare-const amountSpent Int)
(assert (= amountSpent
(+ (x1Pricing x1Qty)
(* x2Qty (getPrice x2))
(* x3Qty (getPrice x3))
(* x4Qty (getPrice x4))
(* x5Qty (getPrice x5)))
))
(assert (< amountSpent moneyToSpend))
感觉事情非常不同,我想知道是否需要。比如有没有办法让 x1Pricing
成为 Offer
数据类型的一部分,如果是这样,那是不是一个好方法?
我的直觉也是将事物放入列表中并在它们之上进行映射等等;但是当我看到 z3 有一个本地 List 实现时,我无法理解如何使用它或者它是否合适(使用 select
和特定索引号编写 assert
s 似乎与完全单独的常量)。
所以我的问题是,是否有更优化的方式来完成我想做的事情?
从一些 Z3Py 示例中我看到我似乎可以通过使用 Python 列表理解和基于索引关联事物来减少一些重复,但我也想知道我是否错过了大局这里是我要去的方向。谢谢。
编辑:我添加了一个非常dumb/naive的运费计算方法和每轮成本计算:
(declare-const shippingCost Int)
(assert (= shippingCost
; FooMart charges a flat shipping fee
(+ (if (or (= (getRetailer x1) FooMart)
(= (getRetailer x2) FooMart)
(= (getRetailer x3) FooMart)
(= (getRetailer x4) FooMart)
(= (getRetailer x5) FooMart))
7500
0
)
; BazTrading charges a flat + per 100 rounds
(if (or (= (getRetailer x1) BazTrading)
(= (getRetailer x2) BazTrading)
(= (getRetailer x3) BazTrading)
(= (getRetailer x4) BazTrading)
(= (getRetailer x5) BazTrading))
2000
0
)
(* (/ (+ (if (= (getRetailer x1) BazTrading)
(getRounds x1)
0)
(if (= (getRetailer x2) BazTrading)
(getRounds x2)
0)
(if (= (getRetailer x3) BazTrading)
(getRounds x3)
0)
(if (= (getRetailer x4) BazTrading)
(getRounds x4)
0)
(if (= (getRetailer x5) BazTrading)
(getRounds x5)
0))
100)
500)
)
))
(declare-const amountSpent Int)
(assert (= amountSpent
(+ (x1Pricing x1Qty)
(* x2Qty (getPrice x2))
(* x3Qty (getPrice x3))
(* x4Qty (getPrice x4))
(* x5Qty (getPrice x5))
shippingCost)))
(assert (< amountSpent moneyToSpend))
(declare-const totalRounds Int)
(assert (= totalRounds
(+ (* x1Qty (getRounds x1))
(* x2Qty (getRounds x2))
(* x3Qty (getRounds x3))
(* x4Qty (getRounds x4))
(* x5Qty (getRounds x5)))
))
(declare-const amountSpentPerRound Real)
(assert (= amountSpentPerRound
(div amountSpent totalRounds)
))
(assert (> amountSpent 20000))
(assert (< amountSpent 30000))
(minimize amountSpentPerRound)
(请注意,该模型在 rise4fun 在线编辑器中无法满足,但在我使用最新 Z3 源的本地机器上运行良好)
现在我真的认为我做错了什么,因为 shippingCost
总是 12600。当我 fiddle 和 amountSpent
最大和最小值断言试图将它推入仅使用 BazTrading 或 FooMart Offers(以降低运费),它会在我杀死它之前挂起几分钟。
SMTLib 不适合编写此类规范。它应该更多地被视为一种 "assembly" 语言,并且应该从其他工具生成。这就是高级 API 发挥作用的地方,具体取决于您选择的语言。有来自 C、C++、Java、Python、Haskell、Scala 的高级绑定。您基本上应该使用那些 APIs 来生成约束并使用 z3通过 API。每个绑定都有自己的 advantages/disadvantages,因此我建议从您最熟悉的宿主语言开始并使用它。