有没有办法将 scipy.optimize.minimize 与 2D np 数组一起用作 x0?

Is there a way to use scipy.optimize.minimize with 2D np array as x0?

我是 python 提供的优化技术的新手,我找不到根据我的需要从 scipy 调整 optimize.minimize 的方法。

我想做的是通过改变利率(所以利率将是我的 x0)来最小化给定债券集的市场价格和模型价格之间的平方差之和。这个总和是通过 price_eval 计算的,它以很少的列表或嵌套列表作为参数,例如每个债券的现金流量嵌套列表。为了使后者更清楚——如果我有 3 种债券,它们有 3、2 和 4 种未来现金流量,如 a、b、c; d,e; h;i;j;k,参数之一是列表 CF_list = [[a,b,c],[d,e],[h;i;j;kl]].

这是一个函数:

def price_eval(rates_list, ttm_list, price_list, CF_list, CF_time_list, N_cf_list):

    price_model_list = []
    sum_squares = 0

    for i in range(0, len(price_list)):
        
        price = 0
        number_cf_per_bond = N_cf_list[i]
        
        for j in range(0, number_cf_per_bond):
            
            price += (CF_list[i][j]) / math.pow(1+rates_list[i][j],CF_time_list[i][j])
   
        diff = price_model_list[i] - price_list[i]
        sum_squares += math.pow(diff,2)

    return sum_squares

我试图将求解器定义为

res = scipy.optimize.minimize(price_eval, rates_guess, args=(ttm_list, price_list, CF_list, CF_time_list, N_cf_list))

其中我提供嵌套列表 rates_guess 作为求解器 rates_list[i][j] 的初始猜测(如果我将其更改为 numpy 数组或不,给出相同的结果)。整个问题可能来自于 scipy.optimize.minimize 仅将一维数组作为 x0,而在我的情况下 x0 需要是二维的,因为每个债券每个现金存储的数据流动。我收到一个错误

...
grad[k] = (f(*((xk + d,) + args)) - f0) / d[k]

TypeError: can only concatenate list (not "float") to list

所以,我的问题是是否有人知道如何解决这个问题?或者是否有可能以某种方式调整求解器,我可以提供一个嵌套的 list/2D np 数组作为 x0,它应该以相同的格式返回新值?如果没有,我还能做什么?

非常感谢您的帮助!

rates_listrates_guess都需要是1darrays/lists。我通常处理这个问题的方法是使用将您喜欢的结构扁平化为列表的函数,然后 'inflate' 将其恢复为所需的形状。所以你可以有,例如,像这样的东西

def to_flat(nested_list):
    '''
    flatten a list of lists
    '''
    flat_list = []
    for l in nested_list:
        for e in l:
            flat_list.append(e)
    return flat_list

# test
rates_nested_list = [[1,1,1],[2,2],[3,3,3,3]]
rates_flat_list = to_flat(rates_nested_list)
print('flat: ',rates_flat_list)

def from_flat(flat_list, list_to_copy_shape_from):
    ''' 
    here the second argument is a nested list in the desired shape -- values not relevant only shape
    '''
    nested_list = []
    idx = 0
    for l in list_to_copy_shape_from:
        nested_list.append([])
        for e in l:
            nested_list[-1].append(flat_list[idx])
            idx += 1
    return nested_list

# test
template = [[0,0,0],[0,0],[0,0,0,0]]
rates_nested_list2 = from_flat(rates_flat_list, template)
print('nested:',rates_nested_list2)

检查输出:

flat:  [1, 1, 1, 2, 2, 3, 3, 3, 3]
nested: [[1, 1, 1], [2, 2], [3, 3, 3, 3]]

现在,在您的 price_eval 函数中,优化器将为您提供一个扁平化的费率列表,您需要 'inflate' 它,因此您的代码将如下所示

def price_eval(rates_flat_list, ttm_list, price_list, CF_list, CF_time_list, N_cf_list):

    # create a nested list of rates from a flat one, using CF_list as a template
    rates_flat_list = from_flat(rates_flat_list,CF_list)

    price_model_list = []
    sum_squares = 0

    for i in range(0, len(price_list)):
        
        price = 0
        number_cf_per_bond = N_cf_list[i]
        
        for j in range(0, number_cf_per_bond):
            
            price += (CF_list[i][j]) / math.pow(1+rates_list[i][j],CF_time_list[i][j])

        # !!! I think your code is missing the following line !!!
        price_model_list.append(price)
        diff = price_model_list[i] - price_list[i]
        sum_squares += math.pow(diff,2)

    return sum_squares

现在,当您调用优化器时,由于 x0 您需要传递一个扁平列表,因此假设 rates_guess 是一个嵌套列表,您将调用

res = scipy.optimize.minimize(price_eval, x0 = to_flat(rates_guess), args=(ttm_list, price_list, CF_list, CF_time_list, N_cf_list))

最后,当你得到 res 时,它将有一个平面列表的解决方案,所以你可能想再次调用 from_flat

最后,由于无论如何您都在进行最小二乘拟合,因此您可能希望使用 scipy.optimize.least_squares 而不是最小化——它具有几乎相同的输入,但在拟合方面应该更好