Adding Newton's Method
Notes
- This part will contain some math you don't necessairly need to understand
- But there will be some that you should.
- I will try to point out which is which as we discuss it.
- I don't care: f(z), f'(z), newton's method.
- I do care: mapping the complex plane to the canvas.
- You need a complex function on which you will do Newton's method.
- f(z) = z3 -1 is sort of the classic function.
- But it would be nice to be able to change this.
- So let's build a file containing the function, and we just need to change the file we include to get a different fractal.
- Into the file function.js
- import the complex number number package
-
import {Complex} from "./complex.js"
-
- Let's define a function to return the function as text
-
export function FunctionName(){ return "F(z) = z^3 - 1" } - You could now use this function to modify the page title
- In
newton.html<h1 id="pageTitle"></h1>
- In
main.js-
import {Complex} from "./complex.js" import {FunctionName} from "./function.js" document.getElementById("pageTitle").innerHTML = FunctionName()
-
- In
-
- Next let's define F and FPrime
- If F(z) = z*z*z -1, F'(z) = 3z*z
- I need the complex constants ONE and THREE to make the code more readable, so just declare these
-
const ONE = new Complex(1,0) const THREE = new Complex(3,0) - Then implement the functions using the complex class
-
// z^3 - 1 export function F(z) { let rv = new Complex(z.Real(), z.Imag()); rv = rv.Mult(rv); // z^2 rv = rv.Mult(z); // z^3 rv = rv.Sub(ONE); return rv; } // 3 z^2 export function FPrime(z) { let rv = new Complex(z.Real(), z.Imag()); rv = rv.Mult(rv); // z^2 rv = rv.Mult(THREE); return rv; } - Note, I had needed to add Real() and Image() as getters to complex for this to work.
- Boy do I miss operator overloading!
- import the complex number number package
- Next, let's work on
newton.js- This will contain the code to do Newton's method
- But we need some other information as well
- We need to know what root the function converged to (for color)
- And we need to know the number of iterations (for intensity of color)
- Include coplex and function
import {Complex} from "./complex.js" import {F, FPrime} from "./function.js" - So I implemented the following functions
- Newton(z):
- takes a complex number
- returns
- The complex root it converges to
- The number of iterations it took to converge.
- Remeber Newton's method is
newZ = z while |F(newZ)| > CONVERGE and iterations < DIVERGE newZ = newZ - F(newZ)/F'(newZ) ++iterations
- I need someconstants for this function
- How close to 0 a root needs to be to converge
- How many iterations it takes to declare the function diverged.
- COLOR_SCALE is a bit strange here
- I want to map iterations to [0,255]
- So I will use COLOR_SCALE to control this.
- The function is
const CONVERGE = 0.001; const COLOR_SCALE = 2; const DIVERGE = 256*COLOR_SCALE; function Newton(z) { let count = 0; let FofZ = F(z); while (FofZ.Mag() > CONVERGE && count < DIVERGE) { let num = F(z); let denom = FPrime(z); if (denom.Real() == 0 && denom.Imag() == 0) { denom = new Complex(1,0) } let fract = num.Div(denom); z = z.Sub(fract); FofZ = F(z); ++count; } return {count: count/COLOR_SCALE, root:z}; } - Note, here I am using an object to return the values
- Consider this just a struct from c++
- Newton(z):
- To test this, I added
export function TestNewton() { let result; let a = new Complex(.4, .4) result = Newton(a) console.log(result.root, result.iterations) }
- We will keep a global array of roots found
let roots = [] - We will need to reset this for each new picture, we will reset these roots
export function ResetRoots() { roots = []; } - Finally we will need to know which root, and maintain the list.
function WhichRoot(answer) { let rootFound = answer.root; let iterations = answer.iterations; if(iterations >= DIVERGE) { return 0; } if (roots.length == 0) { roots.push(rootFound); return 1; } let close = rootFound.Sub(roots[0]).Mag(); let closeNo = 0; for(let i = 1; i < roots.length; ++i) { let newClose = rootFound.Sub(roots[i]).Mag(); if (newClose < close) { closeNo = i; close = newClose; } } if(close <= CONVERGE) { return closeNo+1; } else { roots.push(rootFound); return roots.length; } } - Note this takes advantage of javascript arrays
- reference.
- .length is the size of the array
- .push, .pop
- It returns 0: no root found
- i > 0, the number of the root
-
export function FindRoot(x,y) { let z = new Complex(x,y); let answer = Newton(z); let root = WhichRoot(answer); return {root:root, iterations: answer.iterations} }