aboutsummaryrefslogtreecommitdiff
path: root/circuitpython/extmod/ulab/docs/manual/source/scipy-optimize.rst
blob: 63d60ddaca3bb4f6fb3b9b47c6882b7b565034dc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
scipy.optimize
==============

Functions in the ``optimize`` module can be called by prepending them by
``scipy.optimize.``. The module defines the following three functions:

1. `scipy.optimize.bisect <#bisect>`__
2. `scipy.optimize.fmin <#fmin>`__
3. `scipy.optimize.newton <#newton>`__

Note that routines that work with user-defined functions still have to
call the underlying ``python`` code, and therefore, gains in speed are
not as significant as with other vectorised operations. As a rule of
thumb, a factor of two can be expected, when compared to an optimised
``python`` implementation.

bisect
------

``scipy``:
https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.bisect.html

``bisect`` finds the root of a function of one variable using a simple
bisection routine. It takes three positional arguments, the function
itself, and two starting points. The function must have opposite signs
at the starting points. Returned is the position of the root.

Two keyword arguments, ``xtol``, and ``maxiter`` can be supplied to
control the accuracy, and the number of bisections, respectively.

.. code::
        
    # code to be run in micropython
    
    from ulab import scipy as spy
        
    def f(x):
        return x*x - 1
    
    print(spy.optimize.bisect(f, 0, 4))
    
    print('only 8 bisections: ',  spy.optimize.bisect(f, 0, 4, maxiter=8))
    
    print('with 0.1 accuracy: ',  spy.optimize.bisect(f, 0, 4, xtol=0.1))

.. parsed-literal::

    0.9999997615814209
    only 8 bisections:  0.984375
    with 0.1 accuracy:  0.9375
    
    


Performance
~~~~~~~~~~~

Since the ``bisect`` routine calls user-defined ``python`` functions,
the speed gain is only about a factor of two, if compared to a purely
``python`` implementation.

.. code::
        
    # code to be run in micropython
    
    from ulab import scipy as spy
    
    def f(x):
        return (x-1)*(x-1) - 2.0
    
    def bisect(f, a, b, xtol=2.4e-7, maxiter=100):
        if f(a) * f(b) > 0:
            raise ValueError
    
        rtb = a if f(a) < 0.0 else b
        dx = b - a if f(a) < 0.0 else a - b
        for i in range(maxiter):
            dx *= 0.5
            x_mid = rtb + dx
            mid_value = f(x_mid)
            if mid_value < 0:
                rtb = x_mid
            if abs(dx) < xtol:
                break
    
        return rtb
    
    @timeit
    def bisect_scipy(f, a, b):
        return spy.optimize.bisect(f, a, b)
    
    @timeit
    def bisect_timed(f, a, b):
        return bisect(f, a, b)
    
    print('bisect running in python')
    bisect_timed(f, 3, 2)
    
    print('bisect running in C')
    bisect_scipy(f, 3, 2)

.. parsed-literal::

    bisect running in python
    execution time:  1270  us
    bisect running in C
    execution time:  642  us
    


fmin
----

``scipy``:
https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin.html

The ``fmin`` function finds the position of the minimum of a
user-defined function by using the downhill simplex method. Requires two
positional arguments, the function, and the initial value. Three keyword
arguments, ``xatol``, ``fatol``, and ``maxiter`` stipulate conditions
for stopping.

.. code::
        
    # code to be run in micropython
    
    from ulab import scipy as spy
    
    def f(x):
        return (x-1)**2 - 1
    
    print(spy.optimize.fmin(f, 3.0))
    print(spy.optimize.fmin(f, 3.0, xatol=0.1))

.. parsed-literal::

    0.9996093749999952
    1.199999999999996
    
    


newton
------

``scipy``:https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.newton.html

``newton`` finds a zero of a real, user-defined function using the
Newton-Raphson (or secant or Halley’s) method. The routine requires two
positional arguments, the function, and the initial value. Three keyword
arguments can be supplied to control the iteration. These are the
absolute and relative tolerances ``tol``, and ``rtol``, respectively,
and the number of iterations before stopping, ``maxiter``. The function
retuns a single scalar, the position of the root.

.. code::
        
    # code to be run in micropython
    
    from ulab import scipy as spy
        
    def f(x):
        return x*x*x - 2.0
    
    print(spy.optimize.newton(f, 3., tol=0.001, rtol=0.01))

.. parsed-literal::

    1.260135727246117