Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
PolynomialSplineFunction |
|
| 2.75;2.75 |
1 | /* |
|
2 | * Copyright 2003-2005 The Apache Software Foundation. |
|
3 | * |
|
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 | * you may not use this file except in compliance with the License. |
|
6 | * You may obtain a copy of the License at |
|
7 | * |
|
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
9 | * |
|
10 | * Unless required by applicable law or agreed to in writing, software |
|
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 | * See the License for the specific language governing permissions and |
|
14 | * limitations under the License. |
|
15 | */ |
|
16 | package org.apache.commons.math.analysis; |
|
17 | ||
18 | import java.io.Serializable; |
|
19 | import java.util.Arrays; |
|
20 | ||
21 | import org.apache.commons.math.FunctionEvaluationException; |
|
22 | ||
23 | /** |
|
24 | * Represents a polynomial spline function. |
|
25 | * <p> |
|
26 | * A <strong>polynomial spline function</strong> consists of a set of |
|
27 | * <i>interpolating polynomials</i> and an ascending array of domain |
|
28 | * <i>knot points</i>, determining the intervals over which the spline function |
|
29 | * is defined by the constituent polynomials. The polynomials are assumed to |
|
30 | * have been computed to match the values of another function at the knot |
|
31 | * points. The value consistency constraints are not currently enforced by |
|
32 | * <code>PolynomialSplineFunction</code> itself, but are assumed to hold among |
|
33 | * the polynomials and knot points passed to the constructor. |
|
34 | * <p> |
|
35 | * N.B.: The polynomials in the <code>polynomials</code> property must be |
|
36 | * centered on the knot points to compute the spline function values. See below. |
|
37 | * <p> |
|
38 | * The domain of the polynomial spline function is |
|
39 | * <code>[smallest knot, largest knot]</code>. Attempts to evaluate the |
|
40 | * function at values outside of this range generate IllegalArgumentExceptions. |
|
41 | * <p> |
|
42 | * The value of the polynomial spline function for an argument <code>x</code> |
|
43 | * is computed as follows: |
|
44 | * <ol> |
|
45 | * <li>The knot array is searched to find the segment to which <code>x</code> |
|
46 | * belongs. If <code>x</code> is less than the smallest knot point or greater |
|
47 | * than the largest one, an <code>IllegalArgumentException</code> |
|
48 | * is thrown.</li> |
|
49 | * <li> Let <code>j</code> be the index of the largest knot point that is less |
|
50 | * than or equal to <code>x</code>. The value returned is <br> |
|
51 | * <code>polynomials[j](x - knot[j])</code></li></ol> |
|
52 | * |
|
53 | * @version $Revision$ $Date: 2005-06-26 15:20:57 -0700 (Sun, 26 Jun 2005) $ |
|
54 | */ |
|
55 | public class PolynomialSplineFunction implements UnivariateRealFunction, Serializable { |
|
56 | ||
57 | /** Serializable version identifier */ |
|
58 | static final long serialVersionUID = 7011031166416885789L; |
|
59 | ||
60 | /** Spline segment interval delimiters (knots). Size is n+1 for n segments. */ |
|
61 | private double knots[]; |
|
62 | ||
63 | /** |
|
64 | * The polynomial functions that make up the spline. The first element |
|
65 | * determines the value of the spline over the first subinterval, the |
|
66 | * second over the second, etc. Spline function values are determined by |
|
67 | * evaluating these functions at <code>(x - knot[i])</code> where i is the |
|
68 | * knot segment to which x belongs. |
|
69 | */ |
|
70 | 20 | private PolynomialFunction polynomials[] = null; |
71 | ||
72 | /** |
|
73 | * Number of spline segments = number of polynomials |
|
74 | * = number of partition points - 1 |
|
75 | */ |
|
76 | 20 | private int n = 0; |
77 | ||
78 | ||
79 | /** |
|
80 | * Construct a polynomial spline function with the given segment delimiters |
|
81 | * and interpolating polynomials. |
|
82 | * <p> |
|
83 | * The constructor copies both arrays and assigns the copies to the knots |
|
84 | * and polynomials properties, respectively. |
|
85 | * |
|
86 | * @param knots spline segment interval delimiters |
|
87 | * @param polynomials polynomial functions that make up the spline |
|
88 | * @throws NullPointerException if either of the input arrays is null |
|
89 | * @throws IllegalArgumentException if knots has length less than 2, |
|
90 | * <code>polynomials.length != knots.length - 1 </code>, or the knots array |
|
91 | * is not strictly increasing. |
|
92 | * |
|
93 | */ |
|
94 | 20 | public PolynomialSplineFunction(double knots[], PolynomialFunction polynomials[]) { |
95 | 20 | if (knots.length < 2) { |
96 | 2 | throw new IllegalArgumentException |
97 | ("Not enough knot values -- spline partition must have at least 2 points."); |
|
98 | } |
|
99 | 18 | if (knots.length - 1 != polynomials.length) { |
100 | 2 | throw new IllegalArgumentException |
101 | ("Number of polynomial interpolants must match the number of segments."); |
|
102 | } |
|
103 | 16 | if (!isStrictlyIncreasing(knots)) { |
104 | 2 | throw new IllegalArgumentException |
105 | ("Knot values must be strictly increasing."); |
|
106 | } |
|
107 | ||
108 | 14 | this.n = knots.length -1; |
109 | 14 | this.knots = new double[n + 1]; |
110 | 14 | System.arraycopy(knots, 0, this.knots, 0, n + 1); |
111 | 14 | this.polynomials = new PolynomialFunction[n]; |
112 | 14 | System.arraycopy(polynomials, 0, this.polynomials, 0, n); |
113 | 14 | } |
114 | ||
115 | /** |
|
116 | * Compute the value for the function. |
|
117 | * <p> |
|
118 | * Throws FunctionEvaluationException if v is outside of the domain of the |
|
119 | * function. The domain is [smallest knot, largest knot]. |
|
120 | * <p> |
|
121 | * See {@link PolynomialSplineFunction} for details on the algorithm for |
|
122 | * computing the value of the function. |
|
123 | * |
|
124 | * @param v the point for which the function value should be computed |
|
125 | * @return the value |
|
126 | * @throws FunctionEvaluationException if v is outside of the domain of |
|
127 | * of the spline function (less than the smallest knot point or greater |
|
128 | * than the largest knot point) |
|
129 | */ |
|
130 | public double value(double v) throws FunctionEvaluationException { |
|
131 | 110 | if (v < knots[0] || v > knots[n]) { |
132 | 4 | throw new FunctionEvaluationException(v,"Argument outside domain"); |
133 | } |
|
134 | 106 | int i = Arrays.binarySearch(knots, v); |
135 | 106 | if (i < 0) { |
136 | 40 | i = -i - 2; |
137 | } |
|
138 | //This will handle the case where v is the last knot value |
|
139 | //There are only n-1 polynomials, so if v is the last knot |
|
140 | //then we will use the last polynomial to calculate the value. |
|
141 | 106 | if ( i >= polynomials.length ) { |
142 | 12 | i--; |
143 | } |
|
144 | 106 | return polynomials[i].value(v - knots[i]); |
145 | } |
|
146 | ||
147 | /** |
|
148 | * Returns the derivative of the polynomial spline function as a UnivariateRealFunction |
|
149 | * @return the derivative function |
|
150 | */ |
|
151 | public UnivariateRealFunction derivative() { |
|
152 | 2 | return polynomialSplineDerivative(); |
153 | } |
|
154 | ||
155 | /** |
|
156 | * Returns the derivative of the polynomial spline function as a PolynomialSplineFunction |
|
157 | * |
|
158 | * @return the derivative function |
|
159 | */ |
|
160 | public PolynomialSplineFunction polynomialSplineDerivative() { |
|
161 | 2 | PolynomialFunction derivativePolynomials[] = new PolynomialFunction[n]; |
162 | 8 | for (int i = 0; i < n; i++) { |
163 | 6 | derivativePolynomials[i] = polynomials[i].polynomialDerivative(); |
164 | } |
|
165 | 2 | return new PolynomialSplineFunction(knots, derivativePolynomials); |
166 | } |
|
167 | ||
168 | /** |
|
169 | * Returns the number of spline segments = the number of polynomials |
|
170 | * = the number of knot points - 1. |
|
171 | * |
|
172 | * @return the number of spline segments |
|
173 | */ |
|
174 | public int getN() { |
|
175 | 2 | return n; |
176 | } |
|
177 | ||
178 | /** |
|
179 | * Returns a copy of the interpolating polynomials array. |
|
180 | * <p> |
|
181 | * Returns a fresh copy of the array. Changes made to the copy will |
|
182 | * not affect the polynomials property. |
|
183 | * |
|
184 | * @return the interpolating polynomials |
|
185 | */ |
|
186 | public PolynomialFunction[] getPolynomials() { |
|
187 | 16 | PolynomialFunction p[] = new PolynomialFunction[n]; |
188 | 16 | System.arraycopy(polynomials, 0, p, 0, n); |
189 | 16 | return p; |
190 | } |
|
191 | ||
192 | /** |
|
193 | * Returns an array copy of the knot points. |
|
194 | * <p> |
|
195 | * Returns a fresh copy of the array. Changes made to the copy |
|
196 | * will not affect the knots property. |
|
197 | * |
|
198 | * @return the knot points |
|
199 | */ |
|
200 | public double[] getKnots() { |
|
201 | 2 | double out[] = new double[n + 1]; |
202 | 2 | System.arraycopy(knots, 0, out, 0, n + 1); |
203 | 2 | return out; |
204 | } |
|
205 | ||
206 | /** |
|
207 | * Determines if the given array is ordered in a strictly increasing |
|
208 | * fashion. |
|
209 | * |
|
210 | * @param x the array to examine. |
|
211 | * @return <code>true</code> if the elements in <code>x</code> are ordered |
|
212 | * in a stricly increasing manner. <code>false</code>, otherwise. |
|
213 | */ |
|
214 | private static boolean isStrictlyIncreasing(double[] x) { |
|
215 | 68 | for (int i = 1; i < x.length; ++i) { |
216 | 54 | if (x[i - 1] >= x[i]) { |
217 | 2 | return false; |
218 | } |
|
219 | } |
|
220 | 14 | return true; |
221 | } |
|
222 | } |