Приносим извинения за (возможно, вводящий в заблуждение) заголовок и, возможно, сбивающий с толку вопрос, я очень борюсь с формулировкой моей проблемы и особенно с сжатием ее в одно предложение для заголовка. Я хочу найти корни функции f(w, t, some_other_args) с двумя переменными, w и t, используя python. Реальная структура функции действительно длинная и сложная, вы можете найти ее в конце этого поста. Важно то, что он содержит следующую строку:
k = 1.5 * m.sqrt((1.0 - w) / (1.0 - 0.25 * w))
Это означает, что w не может превышать 1, потому что это привело бы к вычислению квадратного корня из отрицательного числа, что, конечно, невозможно. У меня есть алгоритмы для расчета приблизительных значений w и t с использованием других значений в моей функции, но они очень неточны.
Итак, я пытаюсь вычислить корни с помощью scipy.optimize.fsolve (попробовав буквально все алгоритмы поиска корней, которые я мог найти в Интернете, я обнаружил, что этот лучший для моей функции), используя эти приблизительные значения в качестве отправных точек, которые будут выглядеть так:
solution = optimize.fsolve(f, x0=np.array([t_approx, w_approx]), args=(some_other_args))
Для большинства значений это работает отлично. Однако, если w слишком близко к 1, всегда наступает момент, когда fsolve пробует какое-то значение больше 1 для w, что, в свою очередь, вызывает ValueError (потому что вычисление корня отрицательного числа математически невозможно). Это пример вывода значений, которые использует fsolve, где w должен быть где-то около 0,997:
w_approx: 0.9960090844989311
t_approx: 24.26777844720981
Values: t:24.26777844720981, w:0.9960090844989311
Values: t:24.26777844720981, w:0.9960090844989311
Values: t:24.26777844720981, w:0.9960090844989311
Values: t:24.267778808827888, w:0.9960090844989311
Values: t:24.26777844720981, w:0.996009099340623
Values: t:16.319554685876746, w:1.0096680915775516
solution = optimize.fsolve(f, x0=np.array([t_approx, w_approx]), args=(some_other_args))
File "C:\Users\...\venv\lib\site-packages\scipy\optimize\minpack.py", line 148, in fsolve
res = _root_hybr(func, x0, args, jac=fprime, **options)
File "C:\Users\...\venv\lib\site-packages\scipy\optimize\minpack.py", line 227, in _root_hybr
ml, mu, epsfcn, factor, diag)
File "C:\Users\...\algorithm.py", line 9, in f
k = 1.5 * m.sqrt((1.0 - w) / (1.0 - 0.25 * w))
ValueError: math domain error
Итак, как я могу сказать optimize.fsolve, что w не может быть больше 1? Или каковы альтернативные алгоритмы для выполнения чего-то подобного (я знаю о brentq и так далее, но все они требуют указания интервала для корней оба, чего я не хочу делать)?
Код для тестирования (здесь важно отметить: хотя func теоретически должен рассчитывать R и T с учетом t и w, мне приходится использовать его наоборот. Это немного неуклюже, но мне просто не удается переписать функция, так что она принимает T, R для вычисления t, w - это многовато для моего посредственного математического опыта;)):
import math as m
from scipy import optimize
import numpy as np
def func(t, w, r_1, r_2, r_3):
k = 1.5 * m.sqrt((1.0 - w) / (1.0 - 0.25 * w))
k23 = 2 * k / 3
z1 = 1 / (1 + k23)
z2 = 1 / (1 - k23)
z3 = 3 * ((1 / 5 + r_1 - r_2 - 1 / 5 * r_1 * r_2) / (z1 - r_2 * z2)) * m.exp(t * (k - 1))
z4 = -(z2 - r_2 * z1) / (z1 - r_2 * z2) * m.exp(2 * k * t)
z5 = -(z1 - r_2 * z2) / (z2 - r_2 * z1)
z6 = 3 * (1 - r_2 / 5) / (z2 - r_2 * z1)
beta_t = r_3 / (z2 / z1 * m.exp(2 * k * t) + z5) * (z6 - 3 / (5 * z1) * m.exp(t * (k - 1)))
alpha_t = beta_t * z5 - r_3 * z6
beta_r = (z3 - r_1 / 5 / z2 * m.exp(-2 * t) * 3 - 3 / z2) / (z1 / z2 + z4)
alpha_r = -z1 / z2 * beta_r - 3 / z2 - 3 / 5 * r_1 / z2 * m.exp(-2 * t)
It_1 = 1 / 4 * w / (1 - 8 / 5 * w) * (alpha_t * z2 * m.exp(-k * t) + beta_t * z1 * m.exp(k * t) + 3 * r_3 * m.exp(-t))
Ir_1 = (1 / 4 * w / (1 - 8 / 5 * w)) * (z1 * alpha_r + z2 * beta_r + 3 / 5 + 3 * r_1 * m.exp(-2 * t))
T = It_1 + m.exp(-t) * r_3
R = Ir_1 + m.exp(-2 * t) * r_1
return [T, R]
def calc_1(t, w, T, R, r_1, r_2, r_3):
t_begin = float(t[0])
T_new, R_new = func(t_begin, w, r_1, r_2, r_3)
a = abs(-1 + T_new/T)
b = abs(-1 + R_new/R)
return np.array([a, b])
def calc_2(x, T, R, r_1, r_2, r_3):
t = x[0]
w = x[1]
T_new, R_new = func(t, w, r_1, r_2, r_3)
a = abs(T - T_new)
b = abs(R - R_new)
return np.array([a, b])
def approximate_w(R):
k = (1 - R) / (R + 2 / 3)
w_approx = (1 - ((2 / 3 * k) ** 2)) / (1 - ((1 / 3 * k) ** 2))
return w_approx
def approximate_t(w, T, R, r_1, r_2, r_3):
t = optimize.root(calc_1, x0=np.array([10, 0]), args=(w, T, R, r_1, r_2, r_3))
return t.x[0]
def solve(T, R, r_1, r_2, r_3):
w_x = approximate_w(R)
t_x = approximate_t(w_x, T, R, r_1, r_2, r_3)
sol = optimize.fsolve(calc_2, x0=np.array([t_x, w_x]), args=(T, R, r_1, r_2, r_3))
return sol
# Values for testing:
T = 0.09986490557943692
R = 0.8918728343037964
r_1 = 0
r_2 = 0
r_3 = 1
print(solve(T, R, r_1, r_2, r_3))
Я знаю, что w всегда находится между 0 и 1.
В зависимости от вашей реальной функции вы можете попробовать использовать scipy.optimize.minimze, который позволяет передавать ограничения, возможно, даже least_squares.
Я пробовал их оба некоторое время назад, с ними были другие проблемы, но, честно говоря, я точно не помню - я изучу это еще раз.
использование optimize.minimize вызывает следующую ошибку: File "C:\Users\...\optimize\optimize.py", line 663, in _approx_fprime_helper grad[k] = (f(*((xk + d,) + args)) - f0) / d[k] ValueError: setting an array element with a sequence.






Как насчет логистическийing аргумента, который вы хотите ограничить? Я имею в виду, что внутри f вы могли бы сделать
import numpy as np
def f(free_w, ...):
w = 1/(1 + np.exp(-free_w)) # w will always lie between 0 and 1
...
return zeros
И тогда вам просто нужно применить то же логистическое преобразование к значению решения free_w, чтобы получить w *. Видеть
solution = optimize.fsolve(f, x0=np.array([t_approx, w_approx]), args=(some_other_args))
free_w = solution[0]
w = 1/(1 + np.exp(-free_w))
Есть вопросы @Flob?
Разве вы не потеряете точность, если w близок к 0 или 1, то есть free_w приближается к ± ∞?
@meowgoesthedog Значение все зависит от допуска, установленного вами для вашего zeros. Но на практике и с очень ограниченными значениями tol мои ограничения всегда выполняются задолго до того, как я теряю точность.
как этот free_w = 1/(1 + np.exp(-w)) в конце моего алгоритма?
meowgoesthedog прав, но нет необходимости применять обратную функцию @Flob.
Это действительно интересная идея, спасибо. Но использование этого дает мне математические ошибки в некоторых других точках моих расчетов (например, ZeroDivisionError), а t приближается к -39000, где вместо этого должно быть около 24 ...
@Flob, вы не показали, что такое t и как он соотносится с w. Вставьте функцию в свой пост или добавьте ссылку на нее.
@meowgoesthedog посмотрите мой другой вопрос относительно той же самой проблемы (на которую тогда не было ответа ...)
@Flob. Я хотел бы повторить ваши результаты, но не могу. Какой у вас some_other_args? 1,0,1?
в моем другом вопросе, на который я указал в комментарии выше, я дал некоторые значения для тестирования
Ты сделал. Но это значение не имеет смысла. Ваша система не может быть решена, если некоторые из ее степеней свободы отменены, как это делаете вы.
Я попытался упростить этот вопрос, потому что, как я задал в прошлый раз, я не смог получить ответа. Мне нужно было бы переписать мою функцию, чтобы она могла вычислять w и t в зависимости от R и T, но, глядя на функцию, вы могли бы понять, что это просто невозможно.
@Flob У нас нет времени смотреть на вашего функционального человека. Я просто хочу скопировать все и воспроизвести ваши результаты. Я тебя не понимаю. Вы хотите получить помощь или как?
@Flob Последний совет, который я могу вам дать (в вашем конкретном случае), - не выражать свои ограничения в терминах уровней. Вместо этого выражайте их в относительных терминах. Используя ваш другой вопрос, я имею в виду, сделайте return [-1 + z[0]/T, -1 + z[1]/R]. Ваше здоровье
хм, я только что понял, что в моем старом вопросе есть ошибки. Что ж. Спасибо за вашу помощь, думаю, у меня не будет другого выбора, кроме как добавить код в этот вопрос, хотя я хотел этого избежать.
@Kanak Добавлен полный код для копирования и вставки и воспроизведения моей ошибки выше, если вам все еще интересно
Вы должны попробовать явно определить свою функцию перед ее оптимизацией, чтобы вам было легче проверить домен.
По сути, у вас есть функция T и R. Это сработало для меня:
def func_to_solve(TR_vector, r_1, r_2, r_3):
T, R = TR_vector # what you are trying to find
w_x = approximate_w(R)
t_x = approximate_t(w_x, T, R, r_1, r_2, r_3)
return (calc_2([t_x, w_x], T, R, r_1, r_2, r_3))
def solve(TR, r_1, r_2, r_3):
sol = optimize.fsolve(func_to_solve, x0=TR, args=(r_1, r_2, r_3))
return sol
Также замените m.exp на np.exp.
Спасибо, что нашли время прочитать все это. Я забыл объяснить это: хотя func теоретически должен вычислять T, R с учетом t и w, я должен использовать его наоборот. Это немного неуклюже, но мне просто не удается переписать функцию так, чтобы она принимала T, R для вычисления t, w - это многовато для моих посредственных математических знаний;))
Самым «математически правильным» способом было бы неявно указать эту функцию и позволить программе работать над ней ... конечно, это требует больших затрат числовых затрат. Я понял, что вы использовали приближение, чтобы этого избежать. В любом случае, если вы используете свою псевдо-явную функцию, рекомендуется определить ее перед ее оптимизацией: таким образом вы можете A) построить ее и проверить наличие проблем домена, B) решить ее, используя различные алгоритмы оптимизации. Вы всегда можете использовать что-то вроде scipy.optimize.minimize, которое позволяет использовать границы
Сообщенная вами ошибка возникает, поскольку fsolve не может справиться с неявными ограничениями при преобразовании w в k. Это можно радикально решить, инвертируя эту зависимость, сделав func зависимым от t и k.
def w2k(w): return 3 * m.sqrt((1.0 - w) / (4.0 - w))
#k = 1.5 * m.sqrt((1.0 - w) / (1.0 - 0.25 * w))
# (k/3)**2 * (4-w)= 1-w
def k2w(k): return 4 - 3/(1-(k/3)**2)
def func(t, k, r_1, r_2, r_3):
w = k2w(k)
print "t=%20.15f, k=%20.15f, w=%20.15f"%(t,k,w)
...
Затем удалите абсолютные значения из значений функций в calc1 и calc2. Это только отображает ваши решения как недифференцируемые точки, что плохо для любого алгоритма поиска корней. Изменения знаков и сглаживание корней хороши для методов, подобных Ньютону.
def calc_2(x, T, R, r_1, r_2, r_3):
t = x[0]
k = x[1]
T_new, R_new = func(t, k, r_1, r_2, r_3)
a = T - T_new
b = R - R_new
return np.array([a, b])
Нет особого смысла находить значение для t, решая уравнение, сохраняя w соответственно. k исправлен, он просто удваивает вычислительные усилия.
def approximate_k(R):
k = (1 - R) / (R + 2 / 3)
return k
def solve(T, R, r_1, r_2, r_3):
k_x = approximate_k(R)
t_x = 10
sol = optimize.fsolve(calc_2, x0=np.array([t_x, k_x]), args=(T, R, r_1, r_2, r_3))
return sol
t,k = solve(T, R, r_1, r_2, r_3)
print "t=%20.15f, k=%20.15f, w=%20.15f"%(t, k, k2w(k))
С этими модификациями решение
t= 14.860121342410327, k= 0.026653140486605, w= 0.999763184675043
находится в пределах 15 оценок функций.
Спасибо, что нашли время подумать! Выглядит многообещающе, я попробую. Один вопрос: в solve намеренно ли вы добавляете w_x вместо k_x в массив x0? Если да, то откуда в этом случае берется w_x?
Нет, конечно, нет, на тот момент такая переменная не определена. Я пропустил это при замене имен переменных. Спасибо.
Мне очень нравится это решение, и оно сработало с небольшими изменениями! Спасибо!
У вас есть конечная нижняя граница (n управляемый интерпретацией) для
w?