I just went through the SciML tutorials and now am trying to apply them by building a calculator for solving the compound interest equation. I have two working implementations, but neither seems very convenient. What is the best way to implement this model so that I can plug in all but one variable and solve for whichever one is missing?
First Try
I initially used just NonlinearSolve.jl directly. I wrote a function that steps sequentially through time. I then wrote a wrapper function to isolate the variable I wanted to solve for. Then I constructed an IntervalNonlinearProblem
and solved.
The issues here are that …
- The results overshot in whole year steps because of the steps in my
for
loop. - I have to write a new wrapper function and
IntervalNonlinearProblem
for every variable. (Not implemented.)
Code for First Implementation
using NonlinearSolve
"""
finalvalue(; P=0, M=0, A=0, r=8, t=1) -> F
Calculate the final value of an investment assuming interest is compounded monthly.
# Arguments
- `F`: final value (after `t` years)
- `P`: principal value (before interest)
- `M`: regular monthly contibution
- `A`: regular annual contibution
- `r`: annual interest rate (%)
- `t`: total number of years
"""
function finalvalue(; P=0, M=0, A=0, r=8, t=1)
r /= 100 # convert percent to decimal
F = P # starting value
for _ in 1:t
for _ in 1:12
F *= 1 + r/12 # apply monthly interest
F += M # add monthly contribution
end
F += A # add annual contribution
end
return round(F, digits=2)
end
function findt(t, p)
P = p[1]
M = p[2]
A = p[3]
r = p[4]
F = p[5]
return finalvalue(; P, M, A, r, t) - F
end
p = [5000, 100, 0, 8, 14900]
tspan = (0.0, 10.0)
prob = IntervalNonlinearProblem(findt, tspan, p)
t = round(solve(prob).u, digits=2)
Second Try
Next I found and modeled the equation with ModelingToolkit.This resulted in better accuracy and smaller code. However,
- The only type that seemed applicable is a
NonlinearSystem
, so I have to wrap everything in vectors. - I could not figure out how to create an
IntervalNonlinearProblem
from aNonlinearSystem
, so I am using aNonlinearProblem
instead. This seems like the wrong type of problem based on what I read in the docs. - I don’t know how to switch which variables are
@variables
and which are@parameters
without copying and pasting the entire code over and over and tweaking it. I know of theremake
function, but that seems to only be able to change values not variables.
Code for Second Implementation
using ModelingToolkit, NonlinearSolve
@variables t
@parameters F P A r n
eq = [F ~ P * (1 + r/100/n)^(n*t) +
A * ((1 + r/100/n)^(n*t) - 1) / (r/100/n)]
@named ns = NonlinearSystem(eq, [t], [F, P, A, r, n])
prob = NonlinearProblem(ns, [1], [F=>15000, P=>5000, A=>100, r=>8, n=>12])
sol = round(only(solve(prob).u), digits=2)