比蛮力更快地解决约束满足的方法?

时间:2016-07-13 14:20:47

标签: python constraint-programming

我有一个CSV,为每行提供三个不同x值的y值。当读入pandas DataFrame时,它看起来像这样:

     5     10    20
0 -13.6 -10.7 -10.3
1 -14.1 -11.2 -10.8
2 -12.3  -9.4  -9.0

即,对于第0行,在5处,该值为-13.6,在10处,该值为-10.7,在20处,该值为-10.3。这些值是以下形式的算法的结果:

def calc(x, r, b, c, d):
    if x < 10:
        y = (x * r + b) / x
    elif x >= 10 and x < 20:
        y = ((x * r) + (b - c)) / x
    else:
        y = ((x * r) + (b - d)) / x
    return y

我想找到每行的r,b,c和d的值。我知道每个值的某些事情。例如,对于每一行:r在np.arange( - 。05,-.11,-.01)中,b在np.arange(0,-20.05,-.05)中,c和d在np.arange(0,85,5)。我也知道d是&lt; = c。

目前,我正在用蛮力解决这个问题。对于每一行,我迭代r,b,c和d的每个组合,并测试三个x值处的值是否等于DataFrame中的已知值。这是有效的,除了舍入差异外,每行给出几个基本相同的组合。

问题是这种方法需要花费很长时间才能运行2000多行。我的问题是:有没有比迭代和测试每种组合更快的方法?我的理解是,这是一个约束满足问题,但在那之后,我不知道要缩小什么;有这么多类型的约束满足问题(似乎)我仍然迷失(我甚至不确定这是一个问题!)。任何指导我正确方向的帮助将不胜感激。

1 个答案:

答案 0 :(得分:0)

我希望我能正确理解这项任务。

如果您知道参数的分辨率/离散化,它看起来像离散优化问题(一般来说:),这可以通过CP方法解决。

但是如果你允许这些值是连续的(并重新制定公式),那就是:

  • (1)线性程序:如果检查可行值(需要有效的解决方案)
  • (2)线性程序:如果优化参数以最小化绝对差之和(=误差)
  • (3)二次规划:如果优化参数以最小化平方差之和(=误差)/相当于最小化欧几里德范数

所有三个版本都可以有效解决!

这是(3)使用cvxpy来制定问题和ecos来解决QP的非一般(可以很容易推广)的实现。这两个工具都是开源的。

代码

import numpy as np
import time
from cvxpy import *
from random import uniform

""" GENERATE TEST DATA """
def sample_params():
    while True:
        r = uniform(-0.11, -0.05)
        b = uniform(-20.05, 0)
        c = uniform(0, 85)
        d = uniform(0, 85)
        if d <= c:
            return r, b, c, d

def calc(x, r, b, c, d):
    if x < 10:
        y = (x * r + b) / x
    elif x >= 10 and x < 20:
        y = ((x * r) + (b - c)) / x
    else:
        y = ((x * r) + (b - d)) / x
    return y

N = 2000
sampled_params = [sample_params() for i in range(N)]
data_5 = np.array([calc(5, *sampled_params[i]) for i in range(N)])
data_10 = np.array([calc(10, *sampled_params[i]) for i in range(N)])
data_20 = np.array([calc(20, *sampled_params[i]) for i in range(N)])
data = np.empty((N, 3))
for i in range(N):
    data[i, :] = [data_5[i], data_10[i], data_20[i]]

""" SOLVER """
def solve(row):
    """ vars """
    R = Variable(1)
    B = Variable(1)
    C = Variable(1)
    D = Variable(1)
    E = Variable(3)

    """ constraints """
    constraints = []
    # bounds
    constraints.append(R >= -.11)
    constraints.append(R <= -.05)
    constraints.append(B >= -20.05)
    constraints.append(B <= 0.0)
    constraints.append(C >= 0.0)
    constraints.append(C <= 85.0)
    constraints.append(D >= 0.0)
    constraints.append(D <= 85.0)
    constraints.append(D <= C)
    # formula of model
    constraints.append((1.0 / 5.0) * B + R == row[0] + E[0])  # alternate function form: b/x+r
    constraints.append((1.0 / 10.0)  * B - (1.0 / 10.0) * C == row[1] + E[1])  # alternate function form: b/x-c/x+r
    constraints.append((1.0 / 20.0)  * B - (1.0 / 20.0) * D == row[2] + E[2])  # alternate function form: b/x-d/x+r

    """ Objective """
    objective = Minimize(norm(E, 2))

    """ Solve """
    problem = Problem(objective, constraints)
    problem.solve(solver=ECOS, verbose=False)
    return R.value, B.value, C.value, D.value, E.value

start = time.time()
for i in range(N):
    r, b, c, d, e = solve(data[i])
end = time.time()

print('seconds taken: ', end-start)
print('seconds per row: ', (end-start) / N)

输出

('seconds taken: ', 20.620506048202515)
('seconds per row: ', 0.010310253024101258)