static void Main(){ complex I=new complex(0,1); //complex i complex a=0.01+0.01*I; //start value cvector ya=new cvector(0.100333+0.099666*I,0.999983-0.01*I); //function values of a (from WolframAlpha) complex b=2*PI+2*PI*I; //end value complex h=0.1+0.1*I; //step size double acc=1e-3; //accuracy double eps=1e-3; //accuracy var xs=new List<complex>(); var ys=new List<cvector>(); cvector y=ode.rk23(F,a,ya,b,acc:acc,eps:eps,h:h,xlist:xs,ylist:ys); //solving the ODE Error.WriteLine("Exam project #5: Generalize the ODE solver of your choice to solve ODE with complex-valued functions of complex variable along a straight line between the given start- and end-point.\n"); Error.WriteLine($"accuracy: acc={acc} eps={eps}"); Error.WriteLine($"npoints={xs.Count}"); Error.WriteLine($"start point: a={a}, end point: b={b}"); Error.WriteLine($"end point: b={b}"); Error.WriteLine($"Function values at start point: y0(a)={ya[0]} y1(a)={ya[1]}"); Error.WriteLine($"Function values at end point: y0(b)={y[0]} y1(b)={y[1]}"); Error.WriteLine($"Expected values at end point: sin(b)={sin(b)} cos(b)={cos(b)}"); if(approx(y[0],sin(b),acc,eps) && approx(y[1],cos(b),acc,eps)) Error.WriteLine("test passed"); else Error.WriteLine("test failed"); Error.WriteLine("\nThough the result is very close to the expected result, I can't seem to be able to get it quite right.\n"); for(int i=0;i<xs.Count;i++){ //for making plots WriteLine($"{(xs[i]).Re} {(xs[i]).Im} {(ys[i][0]).Re} {(ys[i][0]).Im} {(ys[i][1]).Re} {(ys[i][1]).Im} {abs(ys[i][0])} {abs(ys[i][1])}"); } }//Main
public static cvector[] rkstep23(Func<complex,cvector,cvector> F, complex x, cvector y, complex h) {// Embedded Runge-Kutta stepper of the order 2-3 cvector k0 = F(x,y); cvector k1 = F(x+h/2, y+(h/2)*k0); cvector k2 = F(x+3*h/4, y+(3*h/4)*k1); cvector ka = (2*k0+3*k1+4*k2)/9; cvector kb = k1; cvector yh = y+ka*h; cvector err = (ka-kb)*h; cvector[] result={yh,err}; return result; }//rkstep23
}//rk23 public static cvector driver(Func<complex,cvector,cvector> F, complex a, cvector ya, complex b, double acc, double eps, complex h, List<complex> xlist, List<cvector> ylist, int limit, Func<Func<complex,cvector,cvector>,complex,cvector,complex,cvector[]> stepper){ int nsteps=0; if(xlist!=null) {xlist.Clear(); xlist.Add(a);} if(ylist!=null) {ylist.Clear(); ylist.Add(ya);} do{ if(abs(a)>abs(b)) return ya; //making sure we have a proper interval complex I=new complex(0,1); //complex i /* enforce that a straight line is taken between a and b */ double relength=b.Re-a.Re; double imlength=b.Im-a.Im; double realres=h.Re/relength; double imres=h.Re/imlength; if(realres<imres) h=realres*relength+realres*imlength*I; else h=imres*relength+imres*imlength*I; /* stop when adding the step means overstepping the interval end value*/ if(abs(a+h)>=abs(b)) return ya; if(a.Re+h.Re>=b.Re) return ya; if(a.Im+h.Im>=b.Im) return ya; /* stop if the number of steps becomes larger than the limit */ if(nsteps>limit){ Error.Write($"ode.driver: nsteps>{limit}\n"); return ya; } cvector[] trial=stepper(F,a,ya,h); //calling rkstep23.cs (calculates the step to be taken) cvector yh=trial[0]; //function values double er=trial[1].norm(); //error double tol=(yh.norm()*eps+acc)*Sqrt(abs(h/(b-a))); //tolerance complex hnew=h*min(pow(tol/er,0.25)*0.95,2); //eq. 40 (the adjusted step) int ok=1; if(er>tol) ok=0; /* updating the x- and y-lists */ if(ok==1){ a+=h; ya=yh; h=hnew; nsteps++; if(xlist!=null) xlist.Add(a); if(ylist!=null) ylist.Add(ya); } /* if precision has not been reached, adjust step size and try again */ else{h=hnew;} //Error.WriteLine($"driver: bad step at {a}"); } }while(true); }//driver23
public static cvector rk23(Func<complex,cvector,cvector> F, complex a, cvector ya, complex b, complex h, double acc, double eps, List<complex> xlist=null, List<cvector> ylist=null, int limit=999){ return driver(F,a,ya,b,acc,eps,h,xlist,ylist,limit,rkstep23); }//rk23
public cvector map(System.Func<complex,complex>f){ cvector v=new cvector(size); for(int i=0;i<size;i++)v[i]=f(this[i]); return v; }
public complex dot(cvector o){ complex sum=0; for(int i=0;i<size;i++)sum+=this[i]*o[i]; return sum; }
public static cvector operator/(cvector v, complex a){ cvector r=new cvector(v.size); for(int i=0;i<v.size;i++)r[i]=v[i]/a; return r; }
public static cvector operator-(cvector v, cvector u){ cvector r=new cvector(v.size); for(int i=0;i<r.size;i++)r[i]=v[i]-u[i]; return r; }
public static cvector operator*(cvector v, double a){ cvector r=new cvector(v.size); for(int i=0;i<v.size;i++) r[i]=a*v[i].Re+a*v[i].Im*I; return r; }
public bool approx(cvector o){ for(int i=0;i<size;i++) if(!approx(this[i],o[i]))return false; return true; }
public static bool approx(cvector a,cvector b,double acc=1e-9,double eps=1e-9){ if(a.size!=b.size)return false; for(int i=0;i<a.size;i++) if(!approx(a[i],b[i],acc,eps))return false; return true; }
public cvector copy(){ cvector b=new cvector(this.size); for(int i=0;i<this.size;i++)b[i]=this[i]; return b; }