Решение нелинейных уравнений в python

У меня есть 4 нелинейных уравнения с тремя неизвестными X , Y и Z , для которых я хочу решить. Уравнения имеют вид:

 F(m) = X^2 + a(m)Y^2 + b(m)XYcosZ + c(m)XYsinZ 

… где a , b и c – константы, зависящие от каждого значения F в четырех уравнениях.

Каков наилучший способ решить это?

  • построение гистограмм, высота баров которых равна 1 в matplotlib
  • Приближение замкнутой кривой к множеству точек
  • Что делать, если я хочу, чтобы 3D сплайн / гладкая интерполяция случайных неструктурированных данных?
  • Почему scipy.ndimage.io.imread возвращает PngImageFile, а не массив значений
  • Как правильно отображать том с некубильными вокселями в майави
  • NumPy PolyFit и PolyVal в нескольких размерах?
  • Matplotlib - Оверлей объема финансирования
  • Эффективно получить индексы гистограмм в Python
  • 2 Solutions collect form web for “Решение нелинейных уравнений в python”

    Есть два способа сделать это.

    1. Использовать нелинейный решатель
    2. Линеаризовать проблему и решить ее в смысле наименьших квадратов

    Настроить

    Итак, насколько я понимаю ваш вопрос, вы знаете F, a, b и c в 4 разных точках и хотите инвертировать параметры модели X, Y и Z. У нас есть 3 неизвестных и 4 наблюдаемых точки данных, поэтому проблема переопределена. Поэтому мы будем решать в смысле наименьших квадратов.

    В этом случае чаще используется противоположная терминология, поэтому давайте перевернем ваше уравнение. Вместо:

     F_i = X^2 + a_i Y^2 + b_i XY cosZ + c_i XY sinZ 

    Давай напишем:

     F_i = a^2 + X_i b^2 + Y_i ab cos(c) + Z_i ab sin(c) 

    Где мы знаем F , X , Y и Z в 4 разных точках (например, F_0, F_1, ... F_i ).

    Мы просто меняем имена переменных, а не само уравнение. (Это больше для моей легкости мышления, чем для чего-либо еще.)

    Линейное решение

    На самом деле можно линеаризовать это уравнение. Вы легко можете решить для a^2 , b^2 , ab cos(c) и ab sin(c) . Чтобы сделать это немного легче, давайте еще раз повторим:

     d = a^2 e = b^2 f = ab cos(c) g = ab sin(c) 

    Теперь уравнение намного проще: F_i = d + e X_i + f Y_i + g Z_i . Легко сделать линейную инверсию наименьших квадратов для d , e , f и g . Затем мы можем получить a , b и c из:

     a = sqrt(d) b = sqrt(e) c = arctan(g/f) 

    Хорошо, давайте напишем это в матричной форме. Мы собираемся перевести 4 наблюдения (код, который мы напишем, будет принимать любое количество наблюдений, но давайте укажем его конкретное на данный момент):

     F_i = d + e X_i + f Y_i + g Z_i 

    В:

     |F_0| |1, X_0, Y_0, Z_0| |d| |F_1| = |1, X_1, Y_1, Z_1| * |e| |F_2| |1, X_2, Y_2, Z_2| |f| |F_3| |1, X_3, Y_3, Z_3| |g| 

    Или: F = G * m (я геофизик, поэтому мы используем G для «функций Грина» и m для «параметров модели». Обычно мы использовали d для «данных» вместо F )

    В python это будет означать:

     def invert(f, x, y, z): G = np.vstack([np.ones_like(x), x, y, z]).T m, _, _, _ = np.linalg.lstsq(G, f) d, e, f, g = m a = np.sqrt(d) b = np.sqrt(e) c = np.arctan2(g, f) # Note that `c` will be in radians, not degrees return a, b, c 

    Нелинейное решение

    Вы также можете решить это, используя scipy.optimize , как предлагал @Joe. Наиболее доступной функцией в scipy.optimize является scipy.optimize.curve_fit которая по умолчанию использует метод scipy.optimize.curve_fit -Marquardt.

    Levenberg-Marquardt – это алгоритм «восхождения на холм» (ну, в данном случае он идет вниз, но этот термин используется в любом случае). В некотором смысле вы делаете первоначальное предположение о параметрах модели (все они по умолчанию в scipy.optimize ) и следуют по склону observed - predicted в вашем пространстве параметров вниз по низу.

    Предостережение: выбор правильного метода нелинейной инверсии, первоначальная догадка и настройка параметров метода – это очень «темное искусство». Вы только учитесь этому, делая это, и есть много ситуаций, когда вещи не будут работать должным образом. Levenberg-Marquardt – хороший общий метод, если пространство параметров довольно гладкое (это должно быть). Есть много других (включая генетические алгоритмы, нейронные сети и т. Д. В дополнение к более распространенным методам, таким как имитированный отжиг), которые лучше в других ситуациях. Я не собираюсь вникать в эту часть здесь.

    Существует одна общая информация о том, что некоторые инструменты оптимизации пытаются исправить для этого scipy.optimize не пытается обрабатывать. Если ваши параметры модели имеют разные величины (например, a=1, b=1000, c=1e-8 ), вам нужно будет перемасштабировать вещи так, чтобы они были одинаковыми по величине. В противном случае scipy.optimize «восхождение по холму» (например, LM) не будут точно рассчитать оценку локального градиента и дадут дико неточные результаты. На данный момент я предполагаю, что a , b и c имеют относительно близкие значения. Кроме того, имейте в виду, что по существу все нелинейные методы требуют от вас первоначального предположения и чувствительны к этой догадки. Я оставляю его ниже (просто передайте его как p0 kwarg в curve_fit ), потому что по умолчанию a, b, c = 1, 1, 1 является довольно точным предположением для a, b, c = 3, 2, 1 ,

    С предостережениями в сторону curve_fit ожидает, что будет передана функция, набор точек, где были сделаны наблюдения (как один ndim x npoints ) и наблюдаемые значения.

    Итак, если мы напишем функцию следующим образом:

     def func(x, y, z, a, b, c): f = (a**2 + x * b**2 + y * a * b * np.cos(c) + z * a * b * np.sin(c)) return f 

    Нам нужно будет обернуть его, чтобы принять несколько разные аргументы, прежде чем передавать его в curve_fit .

    В двух словах:

     def nonlinear_invert(f, x, y, z): def wrapped_func(observation_points, a, b, c): x, y, z = observation_points return func(x, y, z, a, b, c) xdata = np.vstack([x, y, z]) model, cov = opt.curve_fit(wrapped_func, xdata, f) return model 

    Автономный Пример двух методов:

    Чтобы дать вам полную реализацию, вот пример того, что

    1. генерирует случайно распределенные точки для оценки функции on,
    2. оценивает функцию на этих точках (используя параметры модели набора),
    3. добавляет шум к результатам,
    4. а затем инвертирует параметры модели с использованием как линейных, так и нелинейных методов, описанных выше.

     import numpy as np import scipy.optimize as opt def main(): nobservations = 4 a, b, c = 3.0, 2.0, 1.0 f, x, y, z = generate_data(nobservations, a, b, c) print 'Linear results (should be {}, {}, {}):'.format(a, b, c) print linear_invert(f, x, y, z) print 'Non-linear results (should be {}, {}, {}):'.format(a, b, c) print nonlinear_invert(f, x, y, z) def generate_data(nobservations, a, b, c, noise_level=0.01): x, y, z = np.random.random((3, nobservations)) noise = noise_level * np.random.normal(0, noise_level, nobservations) f = func(x, y, z, a, b, c) + noise return f, x, y, z def func(x, y, z, a, b, c): f = (a**2 + x * b**2 + y * a * b * np.cos(c) + z * a * b * np.sin(c)) return f def linear_invert(f, x, y, z): G = np.vstack([np.ones_like(x), x, y, z]).T m, _, _, _ = np.linalg.lstsq(G, f) d, e, f, g = m a = np.sqrt(d) b = np.sqrt(e) c = np.arctan2(g, f) # Note that `c` will be in radians, not degrees return a, b, c def nonlinear_invert(f, x, y, z): # "curve_fit" expects the function to take a slightly different form... def wrapped_func(observation_points, a, b, c): x, y, z = observation_points return func(x, y, z, a, b, c) xdata = np.vstack([x, y, z]) model, cov = opt.curve_fit(wrapped_func, xdata, f) return model main() 

    Вероятно, вы хотите использовать нелинейные решатели Scipy, они очень легкие: http://docs.scipy.org/doc/scipy/reference/optimize.nonlin.html

    Python - лучший язык программирования в мире.