Loading [MathJax]/extensions/tex2jax.js
MRFFL: MR Fortran Finance Library 2024-12-28
Computational Tools For Finance
All Namespaces Files Functions Variables
mrffl_solver_ne.f90
Go to the documentation of this file.
1! -*- Mode:F90; Coding:us-ascii-unix; fill-column:129 -*-
2!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!.H.S.!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!.H.E.!!
3!>
4!! @file mrffl_solver.f90
5!! @author Mitch Richling http://www.mitchr.me/
6!! @date 2024-12-19
7!! @brief Experimental root solvers that take a data payload in addition to a function.@EOL
8!! @keywords finance fortran monte carlo inflation cashflow time value of money tvm percentages taxes stock market
9!! @std F2023
10!! @see https://github.com/richmit/FortranFinance
11!! @copyright
12!! @parblock
13!! Copyright (c) 2024, Mitchell Jay Richling <http://www.mitchr.me/> All rights reserved.
14!!
15!! Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
16!! conditions are met:
17!!
18!! 1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following
19!! disclaimer.
20!!
21!! 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following
22!! disclaimer in the documentation and/or other materials provided with the distribution.
23!!
24!! 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
25!! derived from this software without specific prior written permission.
26!!
27!! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
28!! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29!! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30!! EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
31!! USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32!! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33!! OF THE POSSIBILITY OF SUCH DAMAGE.
34!! @endparblock
35!!
36
37!----------------------------------------------------------------------------------------------------------------------------------
38!> Experimental root solvers that take a data payload in addition to a function.
39!!
40!! The idea is to avoid shared variables. This is especially difficult for nested functions because they require the resulting
41!! binary to have an executable stack -- something frowned upon. Unfortuntly this code currently causes code generated by the
42!! NVIDIA HPC compiler to core dump. That's why this code is "experimental".
43!!
45 use mrffl_config, only: rk=>mrfflrk, ik=>mrfflik
46 implicit none
47 private
48
49 public :: bisection, multi_bisection
50
51contains
52
53 !------------------------------------------------------------------------------------------------------------------------------
54 !> Search for a root for the function f in the interval [x0_init, x1_init].
55 !!
56 !! The process is iterative. At each step the size of the search interval is cut in half. If the search interval gets too
57 !! small (< x_epsilon), then the process is abandoned. If no zero is found after max_itr, then the process is abandoned.
58 !!
59 !! @param xc The solution (or last value tested if no solution is found)
60 !! @param x0_init Left side of search interval
61 !! @param x1_init Right side of search interval
62 !! @param f Function to solve for zero
63 !! @param r_dat Real data passed to f
64 !! @param i_dat Integer data passed to f
65 !! @param x_epsilon Used to test if current search interval is too small
66 !! @param y_epsilon Used to test if f is near zero
67 !! @param max_itr Maximum number of iterations before giving up
68 !! @param status Returns status of computation. 0 if everything worked. Range: 0 & 4001-4032.
69 !! @param progress Print progress as solver searches for a solution
70 !!
71 subroutine bisection(xc, x0_init, x1_init, f, r_dat, i_dat, x_epsilon, y_epsilon, max_itr, status, progress)
72 implicit none
73
74 interface
75 real(kind=rk) function func_to_solve_t(x, r_dat, i_dat)
76 use mrffl_config, only: rk=>mrfflrk, ik=>mrfflik
77 implicit none
78 real(kind=rk), intent(in) :: x
79 real(kind=rk), intent(in) :: r_dat(:)
80 integer(kind=ik), intent(in) :: i_dat(:)
81 end function func_to_solve_t
82 end interface
83
84 procedure(func_to_solve_t) :: f
85 real(kind=rk), intent(in) :: r_dat(:)
86 integer(kind=ik), intent(in) :: i_dat(:)
87 real(kind=rk), intent(out) :: xc
88 real(kind=rk), intent(in) :: x0_init, x1_init
89 real(kind=rk), intent(in) :: x_epsilon, y_epsilon
90 integer(kind=ik), intent(in) :: max_itr
91 integer(kind=ik), intent(out) :: status
92 logical, intent(in) :: progress
93 real(kind=rk) :: x0, x1, f0, f1, fc
94 integer(kind=ik) :: itr
95
96 x0 = x0_init
97 xc = x0
98 f0 = f(xc, r_dat, i_dat)
99 if (abs(f0) < y_epsilon) then
100 status = 0
101 else
102 x1 = x1_init
103 xc = x1
104 f1 = f(xc, r_dat, i_dat)
105 if (abs(f1) < y_epsilon) then
106 xc = x1
107 status = 0
108 else
109 if (progress) print *, 0, x0, f0, x1, f1
110 if (((f0 < 0) .and. (f1 < 0)) .or. ((f0 > 0) .and. (f1 > 0))) then
111 status = 4001 ! "ERROR(bisection): f is the same sign on initial interval endpoints!"
112 else
113 do itr=1,max_itr
114 xc = (x0 + x1) / 2
115 if (abs(x0-x1) < x_epsilon) then
116 status = 4002 ! "ERROR(bisection): Interval is smaller than x_epsilon!"
117 end if
118 fc = f(xc, r_dat, i_dat)
119 if (progress) print *, itr, x0, f0, x1, f1, xc, fc
120 if (abs(fc) < y_epsilon) then
121 status = 0
122 return
123 end if
124 if (fc < 0) then
125 if (f0 < 0) then ! && (f1 > 0)
126 x0 = xc
127 f0 = fc
128 else ! (f0 > 0) && (f1 < 0)
129 x1 = xc
130 f1 = fc
131 end if
132 else ! (f>0)
133 if (f0 < 0) then ! && (f1 > 0)
134 x1 = xc
135 f1 = fc
136 else ! (f0 > 0) && (f1 < 0)
137 x0 = xc
138 f0 = fc
139 end if
140 end if
141 end do
142 status = 4003 ! "ERROR(bisection): Failed to converge!"
143 end if
144 end if
145 end if
146 end subroutine bisection
147
148 !------------------------------------------------------------------------------------------------------------------------------
149 !> Use bisection() to search for a root for the function f in a list of intervals returning the first root found.
150 !!
151 !! @param xc The solution (or last value tested if no solution is found)
152 !! @param x0_init A *VECTOR* of left sides for search intervals
153 !! @param x1_init A *VECTOR* of right sides for search intervals
154 !! @param f Function to solve for zero
155 !! @param r_dat Real data passed to f
156 !! @param i_dat Integer data passed to f
157 !! @param x_epsilon Used to test if current search interval is too small
158 !! @param y_epsilon Used to test if f is near zero
159 !! @param max_itr Maximum number of iterations before giving up
160 !! @param status Returns status of computation. 0 if everything worked. Range: 0 & 4033-4064.
161 !! @param progress Print progress as solver searches for a solution
162 !!
163 subroutine multi_bisection(xc, x0_init, x1_init, f, r_dat, i_dat, x_epsilon, y_epsilon, max_itr, status, progress)
164 implicit none
165
166 interface
167 real(kind=rk) function func_to_solve_t(x, r_dat, i_dat)
168 use mrffl_config, only: rk=>mrfflrk, ik=>mrfflik
169 implicit none
170 real(kind=rk), intent(in) :: x
171 real(kind=rk), intent(in) :: r_dat(:)
172 integer(kind=ik), intent(in) :: i_dat(:)
173 end function func_to_solve_t
174 end interface
175
176 procedure(func_to_solve_t) :: f
177 real(kind=rk), intent(in) :: r_dat(:)
178 integer(kind=ik), intent(in) :: i_dat(:)
179 real(kind=rk), intent(out) :: xc
180 real(kind=rk), intent(in) :: x0_init(:), x1_init(:)
181 real(kind=rk), intent(in) :: x_epsilon, y_epsilon
182 integer(kind=ik), intent(in) :: max_itr
183 integer(kind=ik), intent(out) :: status
184 logical, intent(in) :: progress
185 integer(kind=ik) :: interval, num_intervals
186
187 num_intervals = size(x0_init, kind=ik)
188 do interval=1,num_intervals
189 if (progress) print *, "Bisection on [", x0_init(interval), ", ", x1_init(interval), "]"
190 call bisection(xc, x0_init(interval), x1_init(interval), f, r_dat, i_dat, x_epsilon, y_epsilon, max_itr, &
191 status, progress)
192 if (status == 0) then
193 return
194 end if
195 end do
196 status = 4033 ! "ERROR(bisection): No root found in any interval!"
197 end subroutine multi_bisection
198
199end module mrffl_solver_ne
Configuration for MRFFL (MR Fortran Finance Library).
integer, parameter, public mrfflrk
Real kind used in interfaces.
integer, parameter, public mrfflik
Integer kinds used in interfaces.
Experimental root solvers that take a data payload in addition to a function.
subroutine, public bisection(xc, x0_init, x1_init, f, r_dat, i_dat, x_epsilon, y_epsilon, max_itr, status, progress)
Search for a root for the function f in the interval [x0_init, x1_init].
subroutine, public multi_bisection(xc, x0_init, x1_init, f, r_dat, i_dat, x_epsilon, y_epsilon, max_itr, status, progress)
Use bisection() to search for a root for the function f in a list of intervals returning the first ro...