/* RISO: an implementation of distributed belief networks. * Copyright (C) 1999, Robert Dodier. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307, USA, * or visit the GNU web site, www.gnu.org. */ package riso.numerical; import java.io.*; import riso.general.*; /** An instance of this class represents a monotone cubic spline, * that is, a cubic spline which is either increasing or decreasing between knots. * Formulas are given in F.N. Fritsch and J. Butland, * "A method for constructing local monotone piecewise cubic interpolants", * SIAM J. Scientific and Statistical Computation, 5(2):300--304, 1984. */ public class MonotoneSpline implements Callback_1d, Serializable { int icache = 0; public double[] x, f, d, alpha2, alpha3; public MonotoneSpline( double[] x, double[] f ) throws Exception { if ( x.length < 2 ) throw new Exception( "MonotoneSpline: x.length must be greater than 1." ); this.x = (double[]) x.clone(); this.f = (double[]) f.clone(); d = new double[ x.length ]; alpha2 = new double[ x.length ]; alpha3 = new double[ x.length ]; double[] df = new double[ x.length ]; double[] dx = new double[ x.length ]; for ( int i = 0; i < x.length-1; i++ ) { dx[i] = x[i+1] - x[i]; df[i] = f[i+1] - f[i]; } d[0] = d[x.length-1] = 0; for ( int i = 1; i < x.length-1; i++ ) { double S1 = df[i-1]/dx[i-1]; double S2 = df[i]/dx[i]; double alpha = (1 + dx[i]/(dx[i-1]+dx[i]))/3; if ( S1*S2 > 0 ) d[i] = S1*S2/(alpha*S2+(1-alpha)*S1); else d[i] = 0; } for ( int i = 0; i < x.length-1; i++ ) { double dxi2 = dx[i]*dx[i], dxi3 = dx[i]*dx[i]*dx[i]; alpha2[i] = 3*df[i]/dxi2 - (2*d[i]+d[i+1])/dx[i]; alpha3[i] = -2*df[i]/dxi3 + (d[i]+d[i+1])/dxi2; } // System.err.println( "MonotoneSpline:\n\t"+"x\t"+"f\t"+"d\t"+"a2\t"+"a3" ); // for ( int i = 0; i < x.length; i++ ) // System.err.println( "\t"+x[i]+"\t"+f[i]+"\t"+d[i]+"\t"+alpha2[i]+"\t"+alpha3[i] ); } public double compute_spline( double x, int i ) { double xx = x - this.x[i]; return f[i] + xx*(d[i] + xx*(alpha2[i] + xx*alpha3[i])); } public double f( double x ) throws Exception { // See if x is within the most-recently accessed interval. // If so, compute spline function. If not, search for interval, // then compute spline function. if ( this.x[icache] <= x && x <= this.x[icache+1] ) return compute_spline( x, icache ); else { // Do binary search on intervals. int ilow = 0, ihigh = this.x.length-1; while ( ihigh - ilow > 1 ) { int i = (ilow+ihigh)/2; if ( x < this.x[i] ) ihigh = i; else if ( x > this.x[i] ) ilow = i; else // x == this.x[i] { ilow = i; break; } } icache = ilow; // System.err.println( "MonotoneSpline.f: "+this.x[icache]+" = x["+icache+"] < x = "+x+" < x["+(icache+1)+"] = "+this.x[icache+1] ); return compute_spline( x, icache ); } } public static void main( String[] args ) { int N = 0; String filename = null; for ( int i = 0; i < args.length; i++ ) { switch (args[i].charAt(1)) { case 'N': N = Integer.parseInt(args[++i]); break; case 'f': filename = args[++i]; break; default: System.err.println( "usage: java riso.numerical.MonotoneSpline -N #data -f knots < evaluation-points" ); System.exit(1); } } try { double[] x = new double[N], f = new double[N]; SmarterTokenizer st = new SmarterTokenizer( new InputStreamReader( new FileInputStream( filename ) ) ); int i = 0; for ( st.nextToken(); st.ttype != StreamTokenizer.TT_EOF; st.nextToken() ) { x[i] = Double.parseDouble( st.sval ); st.nextToken(); f[i] = Double.parseDouble( st.sval ); ++i; } double[] xx = new double[i], ff = new double[i]; System.arraycopy( x, 0, xx, 0, i ); System.arraycopy( f, 0, ff, 0, i ); MonotoneSpline s = new MonotoneSpline( xx, ff ); st = new SmarterTokenizer( new InputStreamReader( System.in ) ); for ( st.nextToken(); st.ttype != StreamTokenizer.TT_EOF; st.nextToken() ) { double u = Double.parseDouble( st.sval ); System.err.println( "\t"+u+"\t"+s.f(u) ); } } catch (Exception e) { e.printStackTrace(); } } }