我正在使用向量的自定义实现作为函数,其域是自然数的有限“索引集”,并且其图像是某种类型,可以在其上定义最大值,通常为real
。例如。我可以使用v
和v 1 = 2.7
生成二维向量v 3 = 4.2
。
在这些向量上,我想定义一个类似于运算符的“arg max”,它告诉我上面3
示例中的最大分量v
的索引。我说的是“索引”,因为“arg max”like运算符还会接受一个打破平局功能,以应用于具有值的组件。 (背景为bids in auctions。)
我知道有限集上的Max
是使用fold1
定义的(我还不知道它是如何工作的)。我尝试了这个,这本身就被接受了,但后来对我想做的其他事情没有用处:
fun arg_max_tb :: "index_set ⇒ tie_breaker ⇒ (real vector) ⇒ nat"
where "arg_max_tb N t v = fold1
(λ x y . if (v x > v y) then x (* component values differ *)
else if (v x = v y ∧ t x y) then x (* tie-breaking needed *)
else y) N"
请注意,此外我想证明我的“arg max”运算符的某些属性,这可能需要归纳。我知道有限集的归纳有finite_ne_induct
规则。好的,但我也希望能够以这样的方式定义我的运算符,以便可以对其进行评估(例如,在尝试使用具体的有限集时),但是评估
value "arg_max_tb {1::nat} (op >) (nth [27::real, 42])"
带有预期回报值1
的给出了以下错误:
Wellsortedness error
(in code equation arg_max_tb ?n ?t ?v \equiv
fold1 (\lambda x y. if ord_real_inst.less_real (?v y) (?v x) then ...) ?n):
Type nat not of sort enum
No type arity nat :: enum
因此我采用将有限集转换为列表。在列表中,我已经能够定义运算符,并通过使用list_nonempty_induct
的归纳来证明它的一些属性(如果感兴趣的话可以共享代码)。
基于工作清单的定义如下:
fun arg_max_l_tb :: "(nat list) ⇒ tie_breaker ⇒ (real vector) ⇒ nat"
where "arg_max_l_tb [] t v = 0"
(* in practice we will only call the function
with lists of at least one element *)
| "arg_max_l_tb [x] t v = x"
| "arg_max_l_tb (x # xs) t v =
(let y = arg_max_l_tb xs t v in
if (v x > v y) then x (* component values differ *)
else if (v x = v y ∧ t x y) then x (* tie-breaking needed *)
else y)"
fun arg_max_tb :: "index_set ⇒ tie_breaker ⇒ (real vector) ⇒ nat"
where "arg_max_tb N t v = arg_max_l_tb (sorted_list_of_set N) t v"
我没有成功直接在有限集的构造函数上定义函数。以下不起作用:
fun arg_max_tb :: "index_set ⇒ tie_breaker ⇒ (real vector) ⇒ participant"
where "arg_max_tb {} t b = 0"
| "arg_max_tb {x} t b = x"
| "arg_max_tb (insert x S) t b =
(let y = arg_max_tb S t b in
if (b x > b y) then x
else if (b x = b y ∧ t x y) then x
else y)"
它给我错误信息
Malformed definition:
Non-constructor pattern not allowed in sequential mode.
⋀t b. arg_max_tb {} t b = 0
这可能是因为列表构造函数定义为datatype
,而有限集仅定义为inductive
方案吗?
无论如何 - 你知道在有限集上定义这个函数的方法吗?要么通过直接写下来,要么通过一些类似折叠的效用函数?
答案 0 :(得分:4)
对有限集进行折叠要求结果与访问集合元素的顺序无关,因为集合是无序的。因此,大多数关于fold1 f
的引理假设折叠操作f
是左对齐的,即f a (f b x) = f b (f a x)
对所有a
,b
,x
您在第一个定义中提供给fold1
的函数不满足此要求,因为打破平局函数是一个任意谓词。例如,采取打破平局功能%v v'. True
。因此,如果你想坚持这个定义,你必须首先找到打破平局的充分条件,并通过你所有的引理来解决这个假设。
基于元素排序列表的工作解决方案避免了这种交换性问题。您对{}
,{x}
和insert x S
上的模式匹配的最后建议不起作用有两个原因。首先,fun
只能在数据类型构造函数上进行模式匹配,因此您必须使用function
;这解释了错误消息。但是,你必须证明方程式不重叠,因此你会再次遇到相同的交换问题。此外,您将无法证明终止,因为S
可能是无限的。
代码生成的良好排序错误来自fold1
的设置。 fold1 f A
定义为THE x. fold1Set f A x
fold1Set f A x
,其中x
是f
是A
在某些元素顺序上折叠x
的结果。为了检查所有结果是否相同,生成的代码天真地测试fold1Set f A x
的所有可能值是否x
成立。如果它确实只找到一个这样的值,那么它返回该值。否则,它会引发异常。在您的情况下,nat
是一个索引,即类型为nat
的索引,其中包含无限多的值。因此,无法进行详尽的测试。从技术上讲,这转换为enum
不是类型类fold1
的实例。
通常,您可以根据{{1}}为您定义的所有内容推导出专门的代码方程式。请参阅有关程序优化的代码生成器教程。
答案 1 :(得分:3)
这个问题确实包含多个问题。
通常的递归组合是Finite_Set.fold
(或fold1
)。但是,为了能够证明fold f z S
的任何内容,结果必须独立于f
的元素S
的顺序。
如果f
具有关联性和可交换性,您可以使用Finite_Set.ab_semigroup_mult.fold1_insert
和Finite_Set.fold1_singleton
获取fold1 f S
的简单规则,并且您应该可以使用finite_ne_induct
作为你的归纳规则。
请注意,如果f
是线性顺序,则您给fold1的函数(我称之为t
)只是可交换的:
fun arg_max_tb :: "index_set ⇒ tie_breaker ⇒ (real vector) ⇒ nat"
where "arg_max_tb N t v = fold1
(λ x y . if (v x > v y) then x (* component values differ *)
else if (v x = v y ∧ t x y) then x (* tie-breaking needed *)
else y) N"
这不包含在fold1上的现有引理中,因此您需要证明Finite_Set.ab_semigroup_mult.fold1_insert
的通用变体或插入额外的平局,例如
else if (v x = v y ∧ ~t x y ∧ ~t y x ∧ x < y) then x
如果t
是线性订单,您将能够从simp规则中删除这个额外的决胜局。请注意,这个额外的决胜局基本上是您使用sorted_list_of_set
获得的。
您的arg_max_tb
选择具有特定属性的列表中的一个元素。这也可以使用构造THE x. P x
或SOME x. P x
(选择运算符)直接定义。前者选择满足属性P的唯一元素(如果不存在唯一元素,结果未定义),后者选择满足属性P的某个元素(如果不存在这样的元素,则结果是未定义的)。两者都适用于无限列表。
如果您不需要可执行代码,通常会更好。
由递归定义的函数(即primrec
,fun
或function
)在默认情况下是可执行的(如果其定义中使用的所有函数也是可执行的)。 THE
和SOME
通常只能针对可枚举域执行(这是您从value
获得的错误消息 - nat
不可枚举,因为它不是有限的)。
但是,您始终可以为代码生成器提供函数的替代定义。请参阅函数定义教程,特别是有关细化的部分。
如果您更喜欢带有选择运算符的公式进行证明,而且还希望您的函数可执行,最简单的方法可能是证明arg_max_tb
通过选择和sorted_list_of_set
的定义是等效的。然后,您可以使用[code_unfold]
谓词将选项替换为sorted_list_of_set