使用 Prolog 和 CLP(R) 作为约束系统



我希望使用Prolog生成一个满足约束系统的随机向量。

例如,我们的用户可能会在运行时为我们的软件提供以下信息:

给定一个向量<x1, x2, x3, ... x30>,我们可能有两个约束:

x1 > x2 + x3 + x4
x5 <= sin(x6 + x7)

我想做的是生成一个松散遵循以下形式的Prolog程序:

:- random(0.0, 1.0, X1)
:- random(0.0, 1.0, X2)
#...
# its also concievable that the bounds are different than 0 to 1
:- random(0.0, 1.0, X30)
clp(r) :- constraints { 
X1 > X2 + X3 + X4,
X5 <= sin(X6 + X7)   
}
?- [ X1, X2, X3, X4, ... X30 ]

这将在 30 维空间中输出一个均匀随机向量。

这在Prolog中可行吗?

还有一个消耗该输出的问题。我想做的是调用next()重新生成一个新向量。具体来说,我需要避免重新编译,因为我希望每秒能够生成大约 10,000 个这样的向量。我能达到这种性能水平吗?

我希望在运行我们其余软件的JVM上使用嵌入式(进程内)SWI-Prolog实例。这就够了吗?

没关系!

这种方法原则上是可以的,就我个人而言,我认为Prolog是此类任务的不错选择。

但是,您需要解决几个微妙的问题。

首先,让我们正确理解 CLP(R)的语法

vector([X1,X2,X3,X4,X5,X6,X7]) :- { X1> X2 + X3 + X4, X5 =特别要注意=<的使用以及正确使用{}/1来表示 CLP(R) 约束。令牌<=在Prolog算术中被避免,因为它看起来像一个箭头,通常表示证明者中的暗示

这足以获得第一个答案,即使它们尚未实例化为具体的解决方案

?- 向量(Ls)。Ls = [_1028, _1034, _1040, _1046, _1052, _1058, _1064], {_1046=_1028-_1034-_1040-_1088, _1088>0.0}, {_1052-sin(_1058+_1064)=<0.0}, {_1052-sin(_1058+_1064)=<0.0}, {_1052-sin(_1058+_1064)=<0.0}.

使用random/1我们可以将 (0,1) 中的随机浮点数分配给任何变量。例如:

?- 矢量([A,B,C,D,E,F,G]),  随机(A),  随机(B)。A = 0.33797712696696053, B = 0.7039688010209147,{D= -0.3659916740539542-C-_894, _894>0.0}, {E-sin(F+G)=<0.0}, {E-sin(F+G)=<0.0}, {E-sin(F+G)=<0.0}.

这解决了任务的一部分。但是,这种方法在以下情况下使我们失败:

?- 矢量([A,B,C,D,E,F,G]),  随机(A),  随机(B),  随机(C),  随机(D)。假。

在这里,(确定性!)随机数生成与约束冲突。有几种方法可以解决这个问题。在我展示它们之前,让我们将变量限制在所需的间隔内,例如使用以下定义:

zero_to_one(X) :- { 0 =我们可以简单地将此约束声明为一个附加要求:

?- 矢量([A,B,C,D,E,F,G]),地图列表(zero_to_one, [A,B,C,D,E,F,G]),random(A),  随机(B),  随机(C)。

这再次产生false.

方法1:更多相同

解决上述问题的一种方法是简单地重复随机分配,直到在回溯中找到解决方案:

?- 矢量([A,B,C,D,E,F,G]),  地图列表(zero_to_one, [A,B,C,D,E,F,G]),  随机(A),  随机(B),重复, 随机(C)。 A = 0.9433451780634803, B = 0.15859272177823736, C = 0.706502025956454,{D>=0.0, _2064=0.07825043032878898-D, D<<b>0.07825043032878898}, {E>=0.0, E=<1.0, F>=0.0, F==0.0, G=<1.0, E-sin(...)=<0.0}, {E-sin(F+G)=<0.0}, {E-sin(F+G)=<0.0}, {E-sin(F+G)=<0.0} .

因此,我们离具体的解决方案又近了一步,我们的意思是向量的完整实例化。缺点非常明显:在极端情况下,我们永远不会以这种方式找到有效的作业。运气稍好一点,即使是单个附加变量,也可能需要多次尝试才能找到具体值。

方法 2:最大化或最小化

解决此问题的另一种方法是使用 CLP(R) 的maximize/1和/或minimize/1来使用约束求解器本身来获得具体的解决方案。这仅适用于线性约束,甚至不适用于所有这些约束。例如,请考虑以下查询:

?- { X = sin(Y) },  maplist(zero_to_one, [X,Y]),  最大化(X)。假。

甚至:

?- { X <1 }, maximize(X).

虽然相比之下:

?- { X =<1 }, maximize(X).X = 1.0 .

现在,让我们使用以下技巧来摆脱所有非线性约束:我们简单地将随机浮点数分配给X6X7,例如:

?- 向量(Ls),  Ls = [A,B,C,D,E,F,G],  地图列表(zero_to_one, Ls),随机(F),随机(G)。

在此基础上,我们可以这样写:

?- 向量(Ls),  Ls = [A,B,C,D,E,F,G],  地图列表(zero_to_one, Ls),  随机(F), 随机(G),最大化(A),最小化(B+C+D+E)。Ls = [1.0, 0.0, 0.0, 0.0, 0.0,0.9702069686491169, 0.13220925936558517],A = 1.0, B = C, C = D, D = E, E = 0.0, F = 0.9702069686491169, G = 0.13220925936558517 .

因此,我们得到了一个满足所有约束并具有一些随机分量的具体解决方案。

结语

首先,重复一遍,我认为Prolog是此类任务的不错选择。约束求解器修剪有助于消除大部分搜索空间,约束求解器本身可以帮助您通过最小化和最大化获得具体的解。其次,您仍然需要牢记一些问题:

  • 首先,以这种方式(通过任一方法)生成的解决方案不是随机的,因为每个解决方案的可能性相同 。相反,可能存在比其他解决方案更有可能出现的解决方案集群。
  • 如上所示,方程可能需要一些额外的推理和实验,既可以将它们简化为线性方程,也可以应用适用的优化方向。Prolog非常适合这样的推理,您可以使用它轻松尝试不同的策略。
  • 您可能需要在随机化和确定性优化之间找到一个有效的权衡,以实例化剩余的向量分量。权衡还可能取决于向量分量的纠缠。

最后,一个非常重要的注释:隐式随机状态与我们期望从逻辑关系中获得的属性背道而驰,因为它们可能导致谓词在后续调用中的行为完全不同,从而使调试和系统测试成为一场噩梦。因此,我强烈建议您为随机种子进行配置,或通过代码携带随机 数生成器的显式状态。这将帮助您更好地了解程序的行为,并使其完全确定。稍后可以改变种子以生成不同的解决方案集合。

最新更新