Difference between revisions of "Python:Finding roots"

From PrattWiki
Jump to navigation Jump to search
 
(7 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
== Introduction ==
 
== Introduction ==
The SciPy package [https://docs.scipy.org/doc/scipy-1.1.0/reference/optimize.html scipy.optimize] has several routines for finding roots of equations.  This document is going to focus on the [https://docs.scipy.org/doc/scipy-1.1.0/reference/generated/scipy.optimize.brentq.html#scipy.optimize.brentq brentq] function for finding the root of a single-variable continuous function.  The function can only find one root at a time and it requires brackets for the root.  For open root-finding, use [https://docs.scipy.org/doc/scipy-1.1.0/reference/generated/scipy.optimize.fsolve.html#scipy.optimize.fsolve fsolve].  All the options below for <code>brentq</code> work with <code>fsolve</code>, the only difference is instead of giving a left and right bracket you give a single initial guess.
+
The SciPy package [https://docs.scipy.org/doc/scipy-1.1.0/reference/optimize.html scipy.optimize] has several routines for finding roots of equations.  This document is going to focus on the [https://docs.scipy.org/doc/scipy-1.1.0/reference/generated/scipy.optimize.brentq.html#scipy.optimize.brentq brentq] function for finding the root of a single-variable continuous function.  The function can only find one root at a time and it requires brackets for the root.  For open root-finding, use [https://docs.scipy.org/doc/scipy-1.1.0/reference/generated/scipy.optimize.root.html#scipy.optimize.root root].  All the options below for <code>brentq</code> work with <code>root</code>, the only difference is instead of giving a left and right bracket you give a single initial guess.
  
 
All of the examples assume that
 
All of the examples assume that
 
<syntaxhighlight lang=python>
 
<syntaxhighlight lang=python>
 
import numpy as np
 
import numpy as np
import scipy.optimization as opt
+
import scipy.optimize as opt
 
</syntaxhighlight>
 
</syntaxhighlight>
 
have been called.
 
have been called.
Line 18: Line 18:
 
*DUMMY_VAR is the variable you want to use in this FUNCTION_THING to indicate which of the various inputs <code>brentq</code> is allowed to alter
 
*DUMMY_VAR is the variable you want to use in this FUNCTION_THING to indicate which of the various inputs <code>brentq</code> is allowed to alter
 
*LEFT_BRACKET and RIGHT_BRACKET are values of DUMMY that bracket the root - that is, where FUNCTION_THING has different signs
 
*LEFT_BRACKET and RIGHT_BRACKET are values of DUMMY that bracket the root - that is, where FUNCTION_THING has different signs
**Note that <code>brentq</code> can ''only'' find one root at a time, so you ''cannot'' load this brackets with several values and try to have <code>brentq</code> find multiple results; for that, you need loops
+
**Note that <code>brentq</code> can ''only'' find one root at a time, so you ''cannot'' load the brackets with several values and try to have <code>brentq</code> find multiple results; for that, you need loops
 
*FUNCTION_THING can be a built-in function, a user-defined function, or a calculation - whatever it is that you are trying to set equal to zero; note that DUMMY_VAR must appear somewhere in this expression for <code>brentq</code> to be able to do anything.
 
*FUNCTION_THING can be a built-in function, a user-defined function, or a calculation - whatever it is that you are trying to set equal to zero; note that DUMMY_VAR must appear somewhere in this expression for <code>brentq</code> to be able to do anything.
  
Line 43: Line 43:
 
Note that the dummy variable <code>xi</code> could have been an x here.
 
Note that the dummy variable <code>xi</code> could have been an x here.
  
== Single-Variable Functions ==
+
== Alternate Calls for Single-Variable Functions ==
If you have a function that takes a single variable, there is a slightly simpler way to run <code>brentq</code> - all you need to do is give the name of the variable to FUNCTION_THING rather than setting up the DUMMY_VAR:
+
If you have a function that takes a single variable, there is a slightly simpler way to run <code>brentq</code> - all you need to do is give the name of the function to FUNCTION_THING rather than setting up the DUMMY_VAR:
 
<syntaxhighlight lang=python>
 
<syntaxhighlight lang=python>
 
def fun(x):
 
def fun(x):
Line 53: Line 53:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== Multiple-Variable Functions ==
+
== Alternate Calls for  Multiple-Variable Functions ==
 
If you have a function with multiple variables, first remember that all the other variables need to have known values.  For example, if you have <math>f(x,y)=\cos(x)+\sin(y)</math>, and you want to know where <math>f(x,y)=0</math>, you need to know either <math>x</math> or <math>y</math>.   
 
If you have a function with multiple variables, first remember that all the other variables need to have known values.  For example, if you have <math>f(x,y)=\cos(x)+\sin(y)</math>, and you want to know where <math>f(x,y)=0</math>, you need to know either <math>x</math> or <math>y</math>.   
  
 
=== Unknown as first parameter in function call ===
 
=== Unknown as first parameter in function call ===
Imagine you want to know <math>x</math> when <math>y</math> is 2.5 radians - you can solve this the following ways:
+
Imagine you want to find <math>x</math> when <math>y</math> is 2.5 radians - you can solve this the following ways:
  
 
==== On the fly ====
 
==== On the fly ====
Line 81: Line 81:
  
 
=== Unknown not first parameter in function call ===
 
=== Unknown not first parameter in function call ===
Now imagine you want to know <math>y</math> when <math>x</math> is 2 - you have to create a lambda function for that, either on the fly or based on the defined function:
+
Now imagine you want to find <math>y</math> when <math>x</math> is 2 - you have to create a lambda function for that, either on the fly or based on the defined function:
 
==== On the fly ====
 
==== On the fly ====
 
<syntaxhighlight lang=python>
 
<syntaxhighlight lang=python>
yval = opt.brentq(lambda x: np.cos(2) + np.sin(y), 0, 1)
+
yval = opt.brentq(lambda y: np.cos(2) + np.sin(y), 0, 1)
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==== Defined Function ===
+
==== Defined Function ====
 
<syntaxhighlight lang=python>
 
<syntaxhighlight lang=python>
 
def fun2(x,y):
 
def fun2(x,y):

Latest revision as of 13:09, 9 November 2021

Introduction

The SciPy package scipy.optimize has several routines for finding roots of equations. This document is going to focus on the brentq function for finding the root of a single-variable continuous function. The function can only find one root at a time and it requires brackets for the root. For open root-finding, use root. All the options below for brentq work with root, the only difference is instead of giving a left and right bracket you give a single initial guess.

All of the examples assume that

import numpy as np
import scipy.optimize as opt

have been called.

Most General Case

The following examples show a method that will work regardless of how many input variables your function has or how you define your function - whether it is a built-in or user-defined function. The paradigm is:

ROOT = opt.brentq(lambda DUMMY_VAR: FUNCTION_THING, LEFT_BRACKET, RIGHT_BRACKET)

where

  • ROOT is the calculated value of the requested variable when the function is 0
  • DUMMY_VAR is the variable you want to use in this FUNCTION_THING to indicate which of the various inputs brentq is allowed to alter
  • LEFT_BRACKET and RIGHT_BRACKET are values of DUMMY that bracket the root - that is, where FUNCTION_THING has different signs
    • Note that brentq can only find one root at a time, so you cannot load the brackets with several values and try to have brentq find multiple results; for that, you need loops
  • FUNCTION_THING can be a built-in function, a user-defined function, or a calculation - whatever it is that you are trying to set equal to zero; note that DUMMY_VAR must appear somewhere in this expression for brentq to be able to do anything.

Example

If you want to solve \(f(x)=(x+4)(x-1)=x^2+3x-4=0\) for instance, you have several options:

Function on the Fly

If you only want to solve this problem a couple times, and the calculation only requires one line of code, the process with the least "overhead" involves creating the function on the fly inside the brentq command. All you have to do is put your expression in for the FUNCTION_THING, making sure the DUMMY_VAR is appropriately used - you can use a lambda function for that:

r1 = opt.brentq(lambda x: x**2+3*x-4, -5, -3)
r2 = opt.brentq(lambda x: x**2+3*x-4, 0, 3)

where -5 and -3 were chosen since they bracket the root at -4 and 0 and 3 were chosen since they bracket the root at 1.

Defined Functions

If you are going to solve roots multiple times, you may want to define a function and then call it using a lambda function:

def fun(x):
    return x**2+3*x-4

r1 = opt.brentq(lambda xi: fun(xi), -4, -2)
r2 = opt.brentq(lambda xi: fun(xi), 0, 3)

Note that the dummy variable xi could have been an x here.

Alternate Calls for Single-Variable Functions

If you have a function that takes a single variable, there is a slightly simpler way to run brentq - all you need to do is give the name of the function to FUNCTION_THING rather than setting up the DUMMY_VAR:

def fun(x):
    return x**2+3*x-4

r1 = opt.brentq(fun, -4, -2)
r2 = opt.brentq(fun, 0, 3)

Alternate Calls for Multiple-Variable Functions

If you have a function with multiple variables, first remember that all the other variables need to have known values. For example, if you have \(f(x,y)=\cos(x)+\sin(y)\), and you want to know where \(f(x,y)=0\), you need to know either \(x\) or \(y\).

Unknown as first parameter in function call

Imagine you want to find \(x\) when \(y\) is 2.5 radians - you can solve this the following ways:

On the fly

xval = opt.brentq(lambda x: np.cos(x) + np.sin(2.5), 0, np.pi)

Defined function

def fun2(x,y):
    return np.cos(x)+np.sin(y)

xval = opt.brentq(lambda x: fun2(x, 2.5), 0, np.pi)

or, since the variable you are solving for is first, you can give brentq a tuple with the 2nd (and beyond) variable values:

def fun2(x,y):
    return np.cos(x)+np.sin(y)

xval = opt.brentq(fun2, 0, np.pi, args=(2.5,)) # formally
xval = opt.brentq(fun2, 0, np.pi, 2.5) # less formally since the args positionally come after the right bracket

Unknown not first parameter in function call

Now imagine you want to find \(y\) when \(x\) is 2 - you have to create a lambda function for that, either on the fly or based on the defined function:

On the fly

yval = opt.brentq(lambda y: np.cos(2) + np.sin(y), 0, 1)

Defined Function

def fun2(x,y):
    return np.cos(x)+np.sin(y)

yval = opt.brentq(lambda y: fun2(2, y), 0, 1)



Questions

Post your questions by editing the discussion page of this article. Edit the page, then scroll to the bottom and add a question by putting in the characters *{{Q}}, followed by your question and finally your signature (with four tildes, i.e. ~~~~). Using the {{Q}} will automatically put the page in the category of pages with questions - other editors hoping to help out can then go to that category page to see where the questions are. See the page for Template:Q for details and examples.

External Links

References


Class Document Protection