Аппроксимация векторнозначной многомерной функции с произвольными входными и выходными размерностями в Numpy/Scipy

Отправной точкой является m-мерная вектор-функция

eq  ,

где вход также является n-мерным вектором:

eq.

Вход и выход этой функции — это пустые векторы. Эта функция требует больших затрат для расчета, поэтому мне нужна аппроксимация/интерполяция.

Есть ли функция numpy/scipy, которая возвращает приближение, например. Расширение Тейлора этой функции вблизи заданного значения для x для произвольных размеров m, n?

По сути, я прошу обобщить scipy.interpolate. приблизительно_taylor_polynomial, так как меня также интересуют квадратичные члены приближения.

В scipy.interpolate, кажется, есть некоторые опции для x с векторным значением, но только для скалярных функций, но просто цикл по m компонентам функции не является вариант, так как компоненты не могут быть вычислены отдельно, и функция будет вызываться чаще, чем необходимо.

Если такой функции не существует, быстрый способ с использованием существующих методов и избеганием ненужных вызовов функций также был бы отличным.


person Niklas M.    schedule 19.03.2018    source источник


Ответы (1)


Я думаю, вам нужно сделать собственное приближение для этого. Идея проста: сэмплировать функцию в некоторых разумных точках (по крайней мере, столько, сколько мономов в приближении Тейлора, но желательно больше) и подобрать коэффициенты с np.linalg.lstsq. Фактическая посадка – это одна строчка, остальное – подготовка к ней.

Я буду использовать пример с n=3 и m=2, то есть с тремя переменными и двумерными значениями. Начальная настройка:

import numpy as np
def f(point):
  x, y, z = point[0], point[1], point[2]
  return np.array([np.exp(x + 2*y + 3*z), np.exp(3*x + 2*y + z)]) 
n = 3
m = 2
scale = 0.1

Выбор параметра scale зависит от тех же соображений, что и в строке документации approximate_taylor_polynomial (см. источник).

Следующий шаг — генерация очков. С n переменными квадратичная подгонка включает 1 + n + n*(n+1)/2 мономов (одна константа, n линейных, n(n+1)/2 квадратичных). Я использую 1 + n + n**2 точки, расположенные вокруг (0, 0, 0) и имеющие либо одну, либо две ненулевые координаты. Конкретный выбор несколько произволен; Я не смог найти «канонический» выбор точек выборки для многомерного квадратичного подбора.

points = [np.zeros((n, ))]
points.extend(scale*np.eye(n))
for i in range(n):
    for j in range(n):
        point = np.zeros((n,))
        point[i], point[j] = scale, -scale
        points.append(point)
points = np.array(points)
values = f(points.T).T

Массив values содержит значения функции в каждой из этих точек. Предыдущая строка — единственное место, где вызывается f. Следующий шаг: сгенерируйте мономы для модели и оцените их в тех же точках.

monomials = [np.zeros((1, n)), np.eye(n)]
for i in range(n):
    for j in range(i, n):
        monom = np.zeros((1, n))
        monom[0, i] += 1
        monom[0, j] += 1
        monomials.append(monom)
monomials = np.concatenate(monomials, axis=0)
monom_values = np.prod(points**monomials[:, None, :], axis=-1).T

Давайте рассмотрим ситуацию: у нас есть values функции вида (13, 2) здесь и мономов вида (13, 10). Здесь 13 — количество точек, а 10 — количество мономов. Для каждого столбца values метод lstsq найдет линейную комбинацию столбцов monomials, которая лучше всего аппроксимирует его. Это те коэффициенты, которые нам нужны.

coeffs = np.linalg.lstsq(monom_values, values, rcond=None)[0]

Посмотрим, хороши ли они. Коэффициенты

[[1.         1.        ]
 [1.01171761 3.03011523]
 [2.01839762 2.01839762]
 [3.03011523 1.01171761]
 [0.50041681 4.53385141]
 [2.00667556 6.04011017]
 [3.02759266 3.02759266]
 [2.00667556 2.00667556]
 [6.04011017 2.00667556]
 [4.53385141 0.50041681]]

а массив monomials для справки:

[[0. 0. 0.]
 [1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]
 [2. 0. 0.]
 [1. 1. 0.]
 [1. 0. 1.]
 [0. 2. 0.]
 [0. 1. 1.]
 [0. 0. 2.]]

Так, например, моном x**2, закодированный как [2, 0, 0], получает коэффициенты [0.50041681 4.53385141] для двух компонентов функции f. Это имеет смысл, потому что его коэффициент в разложении Тейлора exp(x + 2*y + 3*z) равен 0,5, а в разложении Тейлора exp(3*x + 2*y + z) — 4,5.

Аппроксимация функции f может быть получена с помощью

def fFit(point,coeffs,monomials):
    return np.prod(point**monomials[:, None, :], axis=-1).T.dot(coeffs)[0]

testpoint = np.array([0.05,-0.05,0.0])

# true value:
print(f(testpoint)) # output: [ 0.95122942  1.0512711 ]

# approximation:
print(fFit(testpoint,coeffs,monomials)) # output: [ 0.95091704  1.05183692]
person Community    schedule 22.03.2018
comment
Спасибо, идеальный ответ! Один небольшой комментарий: существует n(n+1)/2 квадратичных монома, например. 6 для n=3. Не могу отредактировать свой ответ, потому что нужно изменить как минимум 6 символов. - person Niklas M.; 23.03.2018