1 | package 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,…, <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>), for
|
---|
34 | * <I>x</I>∈[<I>x</I><SUB>i-1</SUB>, <I>x</I><SUB>i</SUB>]. </DIV><P></P> For <SPAN CLASS="MATH"><I>x</I> <
|
---|
35 | * <I>x</I><SUB>0</SUB></SPAN> and <SPAN CLASS="MATH"><I>x</I> > <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>ρ</I>∈[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>ρ</I>∑<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>ρ</I>)∫<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>ρ</I> = 1</SPAN>, we obtain the interpolating spline;
|
---|
60 | * and we obtain a linear function by setting <SPAN CLASS="MATH"><I>ρ</I> = 0</SPAN>. The weights <SPAN
|
---|
61 | * CLASS="MATH"><I>w</I><SUB>i</SUB> > 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> int n; <BR> double[] X = new double[n]; <BR> double[] Y = new
|
---|
71 | * double[n]; <BR> // here, fill arrays X and Y with n data points (x_i, y_i) <BR> //
|
---|
72 | * The points must be sorted with respect to x_i. <BR> <BR> double rho = 0.1;
|
---|
73 | * <BR> SmoothingCubicSpline fit = new SmoothingCubicSpline(X, Y, rho); <BR> <BR> int
|
---|
74 | * m = 40; <BR> double[] Xp = new double[m+1]; // Xp, Yp are spline
|
---|
75 | * points <BR> double[] Yp = new double[m+1]; <BR> double h = (X[n-1] - X[0]) / m;
|
---|
76 | * // step <BR> <BR> for (int i = 0; i <= m; i++) {
|
---|
77 | * <BR> double z = X[0] + i * h; <BR> Xp[i] = z;
|
---|
78 | * <BR> Yp[i] = fit.evaluate(z);
|
---|
79 | * // evaluate spline at z <BR> } <BR> <BR></TT>
|
---|
80 | * </DIV>
|
---|
81 | *
|
---|
82 | */
|
---|
83 | public 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>ρ</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">> 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>ρ</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> < <I>x</I> <= <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 | }
|
---|