1
votes

Accolade l'une des deux racines de l'algorithme de recherche de racine pour les racines d'une fonction multivariée

Toutes mes excuses pour le titre (peut-être trompeur) et la question probablement déroutante elle-même, j'ai beaucoup de mal à formuler mon problème et surtout à le compresser en une seule phrase pour le titre. Je veux trouver les racines d'une fonction f (w, t, some_other_args) avec deux variables, w et t , en utilisant python. La vraie structure des fonctions est vraiment longue et compliquée, vous pouvez la trouver à la fin de ce post. L'important est qu'il contienne la ligne suivante:

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))

Cela signifie que w ne peut pas dépasser 1, car cela conduirait à calculer la racine carrée d'un nombre négatif, ce qui, bien sûr, est impossible. J'ai des algorithmes pour calculer les valeurs approximatives de w et t en utilisant d'autres valeurs dans ma fonction, mais elles sont très inexactes.

Donc, j'essaye pour calculer les racines avec scipy.optimize.fsolve (après avoir essayé littéralement tous les algorithmes de recherche de racines que j'ai pu trouver en ligne, j'ai trouvé que celui-ci était le meilleur pour ma fonction) en utilisant ces valeurs approximatives comme points de départ, qui ressemblerait à ceci:

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

Pour la plupart des valeurs, cela fonctionne parfaitement. Si w est trop proche de 1, cependant, il arrive toujours un moment où fsolve essaie une valeur supérieure à 1 pour w , qui, dans turn, lève une ValueError (car calculer la racine d'un nombre négatif est mathématiquement impossible). Voici un exemple d'impression des valeurs que fsolve utilise, où w devrait être quelque part autour de 0.997:

solution = optimize.fsolve(f, x0=np.array([t_approx, w_approx]), args=(some_other_args))

Alors, comment puis-je dire à Optimize.fsolve que w ne peut pas être plus grand que 1? Ou quels sont les algorithmes alternatifs pour faire quelque chose comme ça (je connais brentq et ainsi de suite, mais tous nécessitent de donner un intervalle pour les deux racines, ce que je ne sais pas que vous voulez faire.)?


Code de test (Ce qui est important à noter ici: même si func est théoriquement censé calculer R et T étant donné t et w , je dois l'utiliser dans l'autre sens. C'est un peu maladroit, mais je n'y parviens tout simplement pas réécrire la fonction pour qu'elle accepte T, R pour calculer t, w - c'est un peu trop pour mon expertise mathématique médiocre;)):

k = 1.5 * m.sqrt((1.0 - w) / (1.0 - 0.25 * w))


5 commentaires

Avez-vous une limite inférieure finie (n basée sur l'interprétation ) pour w ?


Je sais que w se situe toujours entre 0 et 1.


En fonction de votre fonction réelle, vous pouvez essayer d'utiliser scipy.optimize.minimize , qui permet de passer des contraintes, peut-être même less_squares .


J'ai essayé les deux il y a quelque temps, il y avait d'autres problèmes avec eux, mais je ne me souviens pas exactement - je vais l'examiner à nouveau.


l'utilisation de Optimize.minimize génère l'erreur suivante: File "C: \ Users \ ... \ Optimize \ Optimize.py", ligne 663, dans _approx_fprime_helper grad [k] = (f ( * ((xk + d,) + args)) - f0) / d [k] ValueError: définition d'un élément de tableau avec une séquence.


3 Réponses :


3
votes

Qu'en est-il de logistique l'argument que vous voulez contraindre? Je veux dire, à l'intérieur de f , vous pourriez faire

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))

Et puis, il vous suffirait d'appliquer la même transformation logistique à la valeur de la solution de free_w pour obtenir w *. Voir

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


16 commentaires

Une question @Flob?


Ne perdriez-vous pas la précision si w est proche de 0 ou 1, c'est-à-dire que free_w s'approche de ± ∞?


@meowgoesthedog Cela tout dépend de la tolérance que vous avez définie pour vos zéros . Mais en pratique, et avec des valeurs tol très restrictives, j'ai toujours mes contraintes respectées bien avant de perdre de la précision.


comme ça free_w = 1 / (1 + np.exp (-w)) à la fin de mon algorithme?


meow va le chien a raison, mais il n'est pas nécessaire d'appliquer la fonction réciproque @Flob.


C'est une idée vraiment intéressante, merci. Mais utiliser cela me donne des erreurs mathématiques à certains autres points de mon calcul ( ZeroDivisionError par exemple) et t se rapproche de -39000, où il devrait plutôt être autour de 24 ...


@Flob vous n'avez pas montré ce qu'est t et comment il se rapporte à w . Collez la fonction dans votre message ou ajoutez un lien vers celui-ci.


@meowgoesthedog regarde mon autre question < / a> concernant exactement le même problème (qui n'a pas obtenu de réponse à l'époque ...)


@Flob. J'aimerais reproduire vos résultats mais je ne peux pas. Quel est votre some_other_args ? 1,0,1 ?


dans mon autre question, que j'ai liée dans le commentaire ci-dessus, j'ai donné quelques valeurs pour tester


Tu l'as fait. Mais ces valeurs n'ont aucun sens. Votre système ne peut pas être résolu si certains de ses degrés de liberté sont annulés comme vous le faites.


J'ai essayé de simplifier le tout pour cette question, parce que la façon dont j'ai posé la dernière fois, je n'ai pas pu obtenir de réponse. La chose que je devrais faire est de réécrire ma fonction pour être capable de calculer w et t en dépendance de R et T < / code>, mais en regardant la fonction, vous comprendrez peut-être que c'est tout simplement impossible.


@Flob Nous n'avons pas le temps de regarder votre homme de fonction. Je veux juste copier le tout et reproduire vos résultats. Je ne te comprends pas. Voulez-vous obtenir de l'aide ou quoi?


@Flob Le dernier conseil que je peux vous donner (dans votre cas précis) est de ne pas exprimer vos contraintes en termes de niveau. Au lieu de cela, exprimez-les en termes relatifs. En utilisant votre autre question, je veux dire, renvoie [-1 + z [0] / T, -1 + z [1] / R] . Acclamations


hm, je viens de réaliser que ma vieille question contient des erreurs. Bien. Merci pour votre aide, je suppose que je n'aurai pas d'autre choix que d'ajouter le code à cette question même si je voulais éviter cela


@Kanak Ajout du code complet pour copier et coller et reproduire mon erreur ci-dessus, si vous êtes toujours intéressé



1
votes

Vous devriez essayer de définir explicitement votre fonction avant de l'optimiser, de cette façon vous pourrez vérifier plus facilement le domaine.

Essentiellement, vous avez une fonction de T et R. cela a fonctionné pour moi:

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

Remplacez également m.exp par np.exp


2 commentaires

Merci d'avoir pris le temps de lire tout cela. J'ai oublié d'expliquer ceci: même si func est théoriquement censé calculer T, R étant donné t et w , je dois l'utiliser dans l'autre sens. C'est un peu maladroit, mais je n'arrive tout simplement pas à réécrire la fonction pour qu'elle accepte T, R pour calculer t, w - c'est un peu trop pour mon expertise mathématique médiocre;))


La manière la plus "mathématiquement correcte" serait de mettre cette fonction implicitement et de laisser le programme le faire fonctionner .. bien sûr, cela coûte numériquement. J'ai compris que vous utilisiez une approximation pour éviter cela. Dans tous les cas, si vous utilisez votre fonction pseudo-explicite, c'est une bonne pratique de la définir avant de l'optimiser: de cette façon, vous pouvez A) le tracer et vérifier les problèmes de domaine, B) le résoudre en utilisant différents algorithmes d'optimisation. Vous pouvez toujours utiliser quelque chose comme scipy.optimize.minimize qui vous permet d'utiliser des limites



1
votes

Votre erreur signalée se produit car fsolve ne peut pas gérer les restrictions implicites lors de la conversion de w en k . Cela peut être résolu radicalement en inversant cette dépendance, rendant func dépendant de t et k à la place.

t=  14.860121342410327, k=   0.026653140486605, w=   0.999763184675043

Supprimez ensuite les valeurs absolues des valeurs de fonction dans calc1 et calc2 . Cela ne rend vos solutions que comme des points non différentiables, ce qui est mauvais pour tout algorithme de recherche de racine. Les changements de signe et les racines lisses sont bons pour les méthodes de type Newton.

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))

Cela n'a pas beaucoup de sens de trouver la valeur de t en résolvant l'équation garder w resp. k corrigé, cela ne fait que doubler l'effort de calcul.

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])

Avec ces modifications, la solution

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)
    ...

se trouve dans 15 évaluations de fonctions.


3 commentaires

Merci d'avoir pris le temps d'y réfléchir! Ça a l'air prometteur, je vais l'essayer rn. Une question: dans résoudre , est-il intentionnel de donner w_x au lieu de k_x dans le tableau x0 ? si oui, d'où vient w_x dans ce cas?


Non, bien sûr que non, il n'y a pas de telle variable définie à ce stade. J'ai manqué cela lors du remplacement des noms de variables. Merci.


J'aime vraiment cette solution et elle a fonctionné avec quelques petites modifications! Merci!