source: src/main/java/negotiator/boaframework/offeringstrategy/anac2013/TheFawkes/SmoothingCubicSpline.java

Last change on this file was 127, checked in by Wouter Pasman, 6 years ago

#41 ROLL BACK of rev.126 . So this version is equal to rev. 125

File size: 15.2 KB
Line 
1package negotiator.boaframework.offeringstrategy.anac2013.TheFawkes;
2
3/*
4 * Class: SmoothingCubicSpline
5 * Description: smoothing cubic spline algorithm of Schoenberg
6 * Environment: Java
7 * Software: SSJ
8 * Copyright (C) 2001 Pierre L'Ecuyer and Université de Montréal
9 * Organization: DIRO, Université de Montréal
10 * @author
11
12 * SSJ is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU General Public License (GPL) as published by the
14 * Free Software Foundation, either version 3 of the License, or
15 * any later version.
16
17 * SSJ is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21
22 * A copy of the GNU General Public License is available at
23 <a href="http://www.gnu.org/licenses">GPL licence site</a>.
24 */
25/**
26 * Represents a cubic spline with nodes at <SPAN CLASS="MATH">(<I>x</I><SUB>i</SUB>, <I>y</I><SUB>i</SUB>)</SPAN>
27 * computed with the smoothing cubic spline algorithm of Schoenberg. A smoothing cubic spline is made of <SPAN
28 * CLASS="MATH"><I>n</I> + 1</SPAN> cubic polynomials. The <SPAN CLASS="MATH"><I>i</I></SPAN>th polynomial of such a
29 * spline, for <SPAN CLASS="MATH"><I>i</I> = 1,&#8230;, <I>n</I> - 1</SPAN>, is defined as <SPAN
30 * CLASS="MATH"><I>S</I><SUB>i</SUB>(<I>x</I>)</SPAN> while the complete spline is defined as
31 *
32 * <P></P> <DIV ALIGN="CENTER" CLASS="mathdisplay"> <I>S</I>(<I>x</I>) =
33 * <I>S</I><SUB>i</SUB>(<I>x</I>),&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for
34 * <I>x</I>&#8712;[<I>x</I><SUB>i-1</SUB>, <I>x</I><SUB>i</SUB>]. </DIV><P></P> For <SPAN CLASS="MATH"><I>x</I> &lt;
35 * <I>x</I><SUB>0</SUB></SPAN> and <SPAN CLASS="MATH"><I>x</I> &gt; <I>x</I><SUB>n-1</SUB></SPAN>, the spline is not
36 * precisely defined, but this class performs extrapolation by using <SPAN CLASS="MATH"><I>S</I><SUB>0</SUB></SPAN> and
37 * <SPAN CLASS="MATH"><I>S</I><SUB>n</SUB></SPAN> linear polynomials. The algorithm which calculates the smoothing
38 * spline is a generalization of the algorithm for an interpolating spline. <SPAN
39 * CLASS="MATH"><I>S</I><SUB>i</SUB></SPAN> is linked to <SPAN CLASS="MATH"><I>S</I><SUB>i+1</SUB></SPAN> at <SPAN
40 * CLASS="MATH"><I>x</I><SUB>i+1</SUB></SPAN> and keeps continuity properties for first and second derivatives at this
41 * point, therefore
42 *
43 * <SPAN CLASS="MATH"><I>S</I><SUB>i</SUB>(<I>x</I><SUB>i+1</SUB>) =
44 * <I>S</I><SUB>i+1</SUB>(<I>x</I><SUB>i+1</SUB>)</SPAN>,
45 *
46 * <SPAN CLASS="MATH"><I>S'</I><SUB>i</SUB>(<I>x</I><SUB>i+1</SUB>) =
47 * <I>S'</I><SUB>i+1</SUB>(<I>x</I><SUB>i+1</SUB>)</SPAN> and <SPAN
48 * CLASS="MATH"><I>S''</I><SUB>i</SUB>(<I>x</I><SUB>i+1</SUB>) =
49 * <I>S''</I><SUB>i+1</SUB>(<I>x</I><SUB>i+1</SUB>)</SPAN>.
50 *
51 * <P> The spline is computed with a smoothing parameter <SPAN CLASS="MATH"><I>&#961;</I>&#8712;[0, 1]</SPAN> which
52 * represents its accuracy with respect to the initial <SPAN CLASS="MATH">(<I>x</I><SUB>i</SUB>,
53 * <I>y</I><SUB>i</SUB>)</SPAN> nodes. The smoothing spline minimizes
54 *
55 * <P></P> <DIV ALIGN="CENTER" CLASS="mathdisplay"> <I>L</I> =
56 * <I>&#961;</I>&sum;<SUB>i=0</SUB><SUP>n-1</SUP><I>w</I><SUB>i</SUB>(<I>y</I><SUB>i</SUB>-<I>S</I><SUB>i</SUB>(<I>x</I><SUB>i</SUB>))<SUP>2</SUP>
57 * + (1 -
58 * <I>&#961;</I>)&int;<SUB>x<SUB>0</SUB></SUB><SUP>x<SUB>n-1</SUB></SUP>(<I>S''</I>(<I>x</I>))<SUP>2</SUP><I>dx</I>
59 * </DIV><P></P> In fact, by setting <SPAN CLASS="MATH"><I>&#961;</I> = 1</SPAN>, we obtain the interpolating spline;
60 * and we obtain a linear function by setting <SPAN CLASS="MATH"><I>&#961;</I> = 0</SPAN>. The weights <SPAN
61 * CLASS="MATH"><I>w</I><SUB>i</SUB> &gt; 0</SPAN>, which default to 1, can be used to change the contribution of each
62 * point in the error term. A large value <SPAN CLASS="MATH"><I>w</I><SUB>i</SUB></SPAN> will give a large weight to the
63 * <SPAN CLASS="MATH"><I>i</I></SPAN>th point, so the spline will pass closer to it. Here is a small example that uses
64 * smoothing splines:
65 *
66 * <P>
67 *
68 * <DIV CLASS="vcode" ALIGN="LEFT"> <TT>
69 *
70 * <BR>&nbsp;&nbsp;&nbsp;int n; <BR>&nbsp;&nbsp;&nbsp;double[] X = new double[n]; <BR>&nbsp;&nbsp;&nbsp;double[] Y = new
71 * double[n]; <BR>&nbsp;&nbsp;&nbsp;// here, fill arrays X and Y with n data points (x_i, y_i) <BR>&nbsp;&nbsp;&nbsp;//
72 * The points must be sorted with respect to x_i. <BR> <BR>&nbsp;&nbsp;&nbsp;double rho = 0.1;
73 * <BR>&nbsp;&nbsp;&nbsp;SmoothingCubicSpline fit = new SmoothingCubicSpline(X, Y, rho); <BR> <BR>&nbsp;&nbsp;&nbsp;int
74 * m = 40; <BR>&nbsp;&nbsp;&nbsp;double[] Xp = new double[m+1]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Xp, Yp are spline
75 * points <BR>&nbsp;&nbsp;&nbsp;double[] Yp = new double[m+1]; <BR>&nbsp;&nbsp;&nbsp;double h = (X[n-1] - X[0]) / m;
76 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// step <BR> <BR>&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt;= m; i++) {
77 * <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;double z = X[0] + i * h; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Xp[i] = z;
78 * <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Yp[i] = fit.evaluate(z);
79 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// evaluate spline at z <BR>&nbsp;&nbsp;&nbsp;} <BR> <BR></TT>
80 * </DIV>
81 *
82 */
83public final class SmoothingCubicSpline
84{
85 private final SmoothingPolynomial[] splineVector;
86 private final double[] x, y, weight;
87 private final double rho;
88
89 /**
90 * Constructs a spline with nodes at <SPAN CLASS="MATH">(<I>x</I><SUB>i</SUB>, <I>y</I><SUB>i</SUB>)</SPAN>, with
91 * weights <SPAN CLASS="MATH"><I>w</I><SUB>i</SUB></SPAN> and smoothing factor <SPAN
92 * CLASS="MATH"><I>&#961;</I></SPAN> = <TT>rho</TT>. The <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> <SPAN
93 * CLASS="textit">must</SPAN> be sorted in increasing order.
94 *
95 * @param x the <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> coordinates.
96 *
97 * @param y the <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN> coordinates.
98 *
99 * @param w the weight for each point, must be <SPAN CLASS="MATH">&gt; 0</SPAN>.
100 *
101 * @param rho the smoothing parameter
102 *
103 * @exception IllegalArgumentException if <TT>x</TT>, <TT>y</TT> and <TT>z</TT> do not have the same length, if rho
104 * has wrong value, or if the spline cannot be calculated.
105 *
106 */
107 public SmoothingCubicSpline( double[] x, double[] y, double[] w, double rho )
108 {
109 if( x.length != y.length )
110 {
111 throw new IllegalArgumentException( "x.length != y.length" );
112 }
113 else if( w != null && x.length != w.length )
114 {
115 throw new IllegalArgumentException( "x.length != w.length" );
116 }
117 else if( rho < 0 || rho > 1 )
118 {
119 throw new IllegalArgumentException( "rho not in [0, 1]" );
120 }
121 else
122 {
123 this.splineVector = new SmoothingPolynomial[x.length + 1];
124 this.rho = rho;
125 this.x = x.clone();
126 this.y = y.clone();
127 this.weight = new double[x.length];
128 if( w == null )
129 {
130 for( int i = 0; i < this.weight.length; i++ )
131 {
132 this.weight[i] = 1;
133 }
134 }
135 else
136 {
137 System.arraycopy( w, 0, this.weight, 0, this.weight.length );
138 }
139 this.resolve();
140 }
141 }
142
143 /**
144 * Constructs a spline with nodes at <SPAN CLASS="MATH">(<I>x</I><SUB>i</SUB>, <I>y</I><SUB>i</SUB>)</SPAN>, with
145 * weights <SPAN CLASS="MATH">= 1</SPAN> and smoothing factor <SPAN CLASS="MATH"><I>&#961;</I></SPAN> =
146 * <TT>rho</TT>. The <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> <SPAN CLASS="textit">must</SPAN> be sorted in
147 * increasing order.
148 *
149 * @param x the <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> coordinates.
150 *
151 * @param y the <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN> coordinates.
152 *
153 * @param rho the smoothing parameter
154 *
155 * @exception IllegalArgumentException if <TT>x</TT> and <TT>y</TT> do not have the same length, if rho has wrong
156 * value, or if the spline cannot be calculated.
157 *
158 */
159 public SmoothingCubicSpline( double[] x, double[] y, double rho )
160 {
161 this( x, y, null, rho );
162 }
163
164 /**
165 * Evaluates and returns the value of the spline at <SPAN CLASS="MATH"><I>z</I></SPAN>.
166 *
167 * @param z argument of the spline.
168 *
169 * @return value of spline.
170 *
171 */
172 public double evaluate( double z )
173 {
174 double returned;
175 int i = this.getFitPolynomialIndex( z );
176 if( i == 0 )
177 {
178 returned = this.splineVector[i].evaluate( z - x[0] );
179 }
180 else
181 {
182 returned = this.splineVector[i].evaluate( z - x[i - 1] );
183 }
184 if( Double.isNaN( returned ) || returned < 0 )
185 {
186 returned = 0;
187 }
188 else if( Double.isInfinite( returned ) || returned > 1 )
189 {
190 returned = 1;
191 }
192 return returned;
193 }
194
195 /**
196 * Returns the index of <SPAN CLASS="MATH"><I>P</I></SPAN>, the {@link Polynomial} instance used to evaluate <SPAN
197 * CLASS="MATH"><I>x</I></SPAN>, in an <TT>ArrayList</TT> table instance returned by
198 * <TT>getSplinePolynomials()</TT>. This index <SPAN CLASS="MATH"><I>k</I></SPAN> gives also the interval in table
199 * <SPAN CLASS="textbf">X</SPAN> which contains the value <SPAN CLASS="MATH"><I>x</I></SPAN> (i.e. such that <SPAN
200 * CLASS="MATH"><I>x</I><SUB>k</SUB> &lt; <I>x</I>&nbsp;&lt;=&nbsp;<I>x</I><SUB>k+1</SUB></SPAN>).
201 *
202 * @return Index of the polynomial check with x in the Polynomial list returned by methodgetSplinePolynomials
203 *
204 */
205 public int getFitPolynomialIndex( double x )
206 {
207 // Algorithme de recherche binaire legerement modifie
208 int j = this.x.length - 1;
209 if( x > this.x[j] )
210 {
211 return j + 1;
212 }
213 int tmp = 0;
214 int i = 0;
215 while( ( i + 1 ) != j )
216 {
217 if( x > this.x[tmp] )
218 {
219 i = tmp;
220 tmp = i + ( ( j - i ) / 2 );
221 }
222 else
223 {
224 j = tmp;
225 tmp = i + ( ( j - i ) / 2 );
226 }
227 if( j == 0 ) // le point est < a x_0, on sort
228 {
229 i--;
230 }
231 }
232 return i + 1;
233 }
234
235 private void resolve()
236 {
237 /*
238 taken from D.S.G Pollock's paper, "Smoothing with Cubic Splines",
239 Queen Mary, University of London (1993)
240 http://www.qmw.ac.uk/~ugte133/PAPERS/SPLINES.PDF
241 */
242 int n = this.x.length;
243 double[] h = new double[n];
244 double[] r = new double[n];
245 double[] u = new double[n];
246 double[] v = new double[n];
247 double[] w = new double[n];
248 double[] q = new double[n + 1];
249 double[] sigma = new double[this.weight.length];
250
251 for( int i = 0; i < sigma.length; i++ )
252 {
253 if( this.weight[i] <= 0 )
254 {
255 sigma[i] = 1.0e100; // arbitrary large number to avoid 1/0
256 }
257 else
258 {
259 sigma[i] = 1 / Math.sqrt( this.weight[i] );
260 }
261 }
262
263 double mu;
264 n = this.x.length - 1;
265 if( this.rho <= 0 )
266 {
267 mu = 1.0e100; // arbitrary large number to avoid 1/0
268 }
269 else
270 {
271 mu = ( 2 * ( 1 - this.rho ) ) / ( 3 * this.rho );
272 }
273
274 h[0] = this.x[1] - this.x[0];
275 r[0] = 3 / h[0];
276 for( int i = 1; i < n; i++ )
277 {
278 h[i] = this.x[i + 1] - this.x[i];
279 r[i] = 3 / h[i];
280 q[i] = ( 3 * ( this.y[i + 1] - y[i] ) / h[i] ) - ( 3 * ( this.y[i] - this.y[i - 1] ) / h[i - 1] );
281 }
282
283 for( int i = 1; i < n; i++ )
284 {
285 u[i] = ( r[i - 1] * r[i - 1] * sigma[i - 1] ) + ( ( r[i - 1] + r[i] ) * ( r[i - 1] + r[i] ) * sigma[i] )
286 + ( r[i] * r[i] * sigma[i + 1] );
287 u[i] = mu * u[i] + 2 * ( this.x[i + 1] - this.x[i - 1] );
288 v[i] = ( -( r[i - 1] + r[i] ) * r[i] * sigma[i] ) - ( r[i] * ( r[i] + r[i + 1] ) * sigma[i + 1] );
289 v[i] = ( mu * v[i] ) + h[i];
290 w[i] = mu * r[i] * r[i + 1] * sigma[i + 1];
291 }
292 q = Quincunx( u, v, w, q );
293
294 // extrapolation a gauche
295 double[] params = new double[4];
296 params[0] = this.y[0] - ( mu * r[0] * q[1] * sigma[0] );
297 double dd1 = this.y[1] - ( mu * ( ( -r[0] - r[1] ) * q[1] + r[1] * q[2] ) * sigma[1] );
298 params[1] = ( dd1 - params[0] ) / h[0] - ( q[1] * h[0] / 3 );
299 this.splineVector[0] = new SmoothingPolynomial( params );
300
301 // premier polynome
302 params[0] = this.y[0] - ( mu * r[0] * q[1] * sigma[0] );
303 double dd2 = this.y[1] - ( mu * ( ( -r[0] - r[1] ) * q[1] + r[1] * q[2] ) * sigma[1] );
304 params[3] = q[1] / ( 3 * h[0] );
305 params[2] = 0;
306 params[1] = ( ( dd2 - params[0] ) / h[0] ) - ( q[1] * h[0] / 3 );
307 this.splineVector[1] = new SmoothingPolynomial( params );
308
309 // les polynomes suivants
310 int j;
311 for( j = 1; j < n; j++ )
312 {
313 params[3] = ( q[j + 1] - q[j] ) / ( 3 * h[j] );
314 params[2] = q[j];
315 params[1] = ( ( q[j] + q[j - 1] ) * h[j - 1] ) + this.splineVector[j].getCoefficient( 1 );
316 params[0] = ( r[j - 1] * q[j - 1] ) + ( ( -r[j - 1] - r[j] ) * q[j] ) + ( r[j] * q[j + 1] );
317 params[0] = this.y[j] - ( mu * params[0] * sigma[j] );
318 this.splineVector[j + 1] = new SmoothingPolynomial( params );
319 }
320
321 // extrapolation a droite
322 j = n;
323 params[3] = 0;
324 params[2] = 0;
325 params[1] = this.splineVector[j].derivative( this.x[n] - this.x[n - 1] );
326 params[0] = this.splineVector[j].evaluate( this.x[n] - this.x[n - 1] );
327 this.splineVector[n + 1] = new SmoothingPolynomial( params );
328 }
329
330 private static double[] Quincunx( double[] u, double[] v, double[] w, double[] q )
331 {
332 u[0] = 0;
333 v[1] /= u[1];
334 w[1] /= u[1];
335 int j;
336 for( j = 2; j < ( u.length - 1 ); j++ )
337 {
338 u[j] = u[j] - ( u[j - 2] * w[j - 2] * w[j - 2] ) - ( u[j - 1] * v[j - 1] * v[j - 1] );
339 v[j] = ( v[j] - ( u[j - 1] * v[j - 1] * w[j - 1] ) ) / u[j];
340 w[j] /= u[j];
341 }
342
343 // forward substitution
344 q[1] -= v[0] * q[0];
345 for( j = 2; j < ( u.length - 1 ); j++ )
346 {
347 q[j] = q[j] - ( v[j - 1] * q[j - 1] ) - ( w[j - 2] * q[j - 2] );
348 }
349 for( j = 1; j < ( u.length - 1 ); j++ )
350 {
351 q[j] /= u[j];
352 }
353
354 // backward substitution
355 q[u.length - 1] = 0;
356 for( j = ( u.length - 3 ); j > 0; j-- )
357 {
358 q[j] = q[j] - ( v[j] * q[j + 1] ) - ( w[j] * q[j + 2] );
359 }
360
361 return q;
362 }
363}
Note: See TracBrowser for help on using the repository browser.