View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.numbers.core;
18  
19  /**
20   * <a href="https://en.wikipedia.org/wiki/Norm_(mathematics)">Norm</a> functions.
21   *
22   * <p>The implementations provide increased numerical accuracy.
23   * Algorithms primary source is the 2005 paper
24   * <a href="https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.2.1547">
25   * Accurate Sum and Dot Product</a> by Takeshi Ogita, Siegfried M. Rump,
26   * and Shin'ichi Oishi published in <em>SIAM J. Sci. Comput</em>.
27   */
28  public enum Norm {
29      /**
30       * <a href="https://en.wikipedia.org/wiki/Norm_(mathematics)#Taxicab_norm_or_Manhattan_norm">
31       *  Manhattan norm</a> (sum of the absolute values of the arguments).
32       */
33      L1(Norm::manhattan, Norm::manhattan, Norm::manhattan),
34      /** Alias for {@link #L1}. */
35      MANHATTAN(L1),
36      /** <a href="https://en.wikipedia.org/wiki/Norm_(mathematics)#Euclidean_norm">Euclidean norm</a>. */
37      L2(Norm::euclidean, Norm::euclidean, Norm::euclidean),
38      /** Alias for {@link #L2}. */
39      EUCLIDEAN(L2),
40      /**
41       * <a href="https://en.wikipedia.org/wiki/Norm_(mathematics)#Maximum_norm_(special_case_of:_infinity_norm,_uniform_norm,_or_supremum_norm)">
42       *  Maximum norm</a> (maximum of the absolute values of the arguments).
43       */
44      LINF(Norm::maximum, Norm::maximum, Norm::maximum),
45      /** Alias for {@link #LINF}. */
46      MAXIMUM(LINF);
47  
48      /**
49       * Threshold for scaling small numbers. This value is chosen such that doubles
50       * set to this value can be squared without underflow. Values less than this must
51       * be scaled up.
52       */
53      private static final double SMALL_THRESH = 0x1.0p-511;
54      /**
55       * Threshold for scaling large numbers. This value is chosen such that 2^31 doubles
56       * set to this value can be squared and added without overflow. Values greater than
57       * this must be scaled down.
58       */
59      private static final double LARGE_THRESH = 0x1.0p+496;
60      /**
61       * Threshold for scaling up a single value by {@link #SCALE_UP} without risking
62       * overflow when the value is squared.
63       */
64      private static final double SAFE_SCALE_UP_THRESH = 0x1.0p-100;
65      /** Value used to scale down large numbers. */
66      private static final double SCALE_DOWN = 0x1.0p-600;
67      /** Value used to scale up small numbers. */
68      private static final double SCALE_UP = 0x1.0p+600;
69  
70      /** Threshold for the difference between the exponents of two Euclidean 2D input values
71       * where the larger value dominates the calculation.
72       */
73      private static final int EXP_DIFF_THRESHOLD_2D = 54;
74  
75      /** Function of 2 arguments. */
76      private final Two two;
77      /** Function of 3 arguments. */
78      private final Three three;
79      /** Function of array argument. */
80      private final Array array;
81  
82      /** Function of 2 arguments. */
83      @FunctionalInterface
84      private interface Two {
85          /**
86           * @param x Argument.
87           * @param y Argument.
88           * @return the norm.
89           */
90          double of(double x, double y);
91      }
92      /** Function of 3 arguments. */
93      @FunctionalInterface
94      private interface Three {
95          /**
96           * @param x Argument.
97           * @param y Argument.
98           * @param z Argument.
99           * @return the norm.
100          */
101         double of(double x, double y, double z);
102     }
103     /** Function of array argument. */
104     @FunctionalInterface
105     private interface Array {
106         /**
107          * @param v Array of arguments.
108          * @return the norm.
109          */
110         double of(double[] v);
111     }
112 
113     /**
114      * @param two Function of 2 arguments.
115      * @param three Function of 3 arguments.
116      * @param array Function of array argument.
117      */
118     Norm(Two two,
119          Three three,
120          Array array) {
121         this.two = two;
122         this.three = three;
123         this.array = array;
124     }
125 
126     /**
127      * @param alias Alternative name.
128      */
129     Norm(Norm alias) {
130         this.two = alias.two;
131         this.three = alias.three;
132         this.array = alias.array;
133     }
134 
135     /**
136      * Computes the norm.
137      *
138      * <p>Special cases:
139      * <ul>
140      *  <li>If either value is {@link Double#NaN}, then the result is {@link Double#NaN}.</li>
141      *  <li>If either value is infinite and the other value is not {@link Double#NaN}, then
142      *   the result is {@link Double#POSITIVE_INFINITY}.</li>
143      * </ul>
144      *
145      * @param x Argument.
146      * @param y Argument.
147      * @return the norm.
148      */
149     public final double of(double x,
150                            double y) {
151         return two.of(x, y);
152     }
153 
154     /**
155      * Computes the norm.
156      *
157      * <p>Special cases:
158      * <ul>
159      *  <li>If any value is {@link Double#NaN}, then the result is {@link Double#NaN}.</li>
160      *  <li>If any value is infinite and no value is not {@link Double#NaN}, then the
161      *   result is {@link Double#POSITIVE_INFINITY}.</li>
162      * </ul>
163      *
164      * @param x Argument.
165      * @param y Argument.
166      * @param z Argument.
167      * @return the norm.
168      */
169     public final double of(double x,
170                            double y,
171                            double z) {
172         return three.of(x, y, z);
173     }
174 
175     /**
176      * Computes the norm.
177      *
178      * <p>Special cases:
179      * <ul>
180      *  <li>If any value is {@link Double#NaN}, then the result is {@link Double#NaN}.</li>
181      *  <li>If any value is infinite and no value is not {@link Double#NaN}, then the
182      *   result is {@link Double#POSITIVE_INFINITY}.</li>
183      * </ul>
184      *
185      * @param v Argument.
186      * @return the norm.
187      * @throws IllegalArgumentException if the array is empty.
188      */
189     public final double of(double[] v) {
190         ensureNonEmpty(v);
191         return array.of(v);
192     }
193 
194     /** Computes the Manhattan norm.
195      *
196      * @param x first input value
197      * @param y second input value
198      * @return \(|x| + |y|\).
199      *
200      * @see #L1
201      * @see #MANHATTAN
202      * @see #of(double,double)
203      */
204     private static double manhattan(final double x,
205                                     final double y) {
206         return Math.abs(x) + Math.abs(y);
207     }
208 
209     /** Computes the Manhattan norm.
210      *
211      * @param x first input value
212      * @param y second input value
213      * @param z third input value
214      * @return \(|x| + |y| + |z|\)
215      *
216      * @see #L1
217      * @see #MANHATTAN
218      * @see #of(double,double,double)
219      */
220     private static double manhattan(final double x,
221                                     final double y,
222                                     final double z) {
223         return Sum.of(Math.abs(x))
224             .add(Math.abs(y))
225             .add(Math.abs(z))
226             .getAsDouble();
227     }
228 
229     /** Computes the Manhattan norm.
230      *
231      * @param v input values
232      * @return \(|v_0| + ... + |v_i|\)
233      *
234      * @see #L1
235      * @see #MANHATTAN
236      * @see #of(double[])
237      */
238     private static double manhattan(final double[] v) {
239         final Sum sum = Sum.create();
240 
241         for (final double d : v) {
242             sum.add(Math.abs(d));
243         }
244 
245         return sum.getAsDouble();
246     }
247 
248     /** Computes the Euclidean norm.
249      * This implementation handles possible overflow or underflow.
250      *
251      * <p><strong>Comparison with Math.hypot()</strong>
252      * While not guaranteed to return the same result, this method provides
253      * similar error bounds as {@link Math#hypot(double, double)} (and may run faster on
254      * some JVM).
255      *
256      * @param x first input
257      * @param y second input
258      * @return \(\sqrt{x^2 + y^2}\).
259      *
260      * @see #L2
261      * @see #EUCLIDEAN
262      * @see #of(double,double)
263      */
264     private static double euclidean(final double x,
265                                     final double y) {
266         final double xabs = Math.abs(x);
267         final double yabs = Math.abs(y);
268 
269         final double max;
270         final double min;
271         // the compare method considers NaN greater than other values, meaning that our
272         // check for if the max is finite later on will detect NaNs correctly
273         if (Double.compare(xabs, yabs) > 0) {
274             max = xabs;
275             min = yabs;
276         } else {
277             max = yabs;
278             min = xabs;
279         }
280 
281         // if the max is not finite, then one of the inputs must not have
282         // been finite
283         if (!Double.isFinite(max)) {
284             // let the standard multiply operation determine whether to return NaN or infinite
285             return xabs * yabs;
286         } else if (Math.getExponent(max) - Math.getExponent(min) > EXP_DIFF_THRESHOLD_2D) {
287             // value is completely dominated by max; just return max
288             return max;
289         }
290 
291         // compute the scale and rescale values
292         final double scale;
293         final double rescale;
294         if (max > LARGE_THRESH) {
295             scale = SCALE_DOWN;
296             rescale = SCALE_UP;
297         } else if (max < SAFE_SCALE_UP_THRESH) {
298             scale = SCALE_UP;
299             rescale = SCALE_DOWN;
300         } else {
301             scale = 1d;
302             rescale = 1d;
303         }
304 
305         // initialise sum and compensation using scaled x
306         final double sx = xabs * scale;
307         double sum = sx * sx;
308         double comp = DD.twoSquareLow(sx, sum);
309 
310         // add scaled y
311         final double sy = yabs * scale;
312         final double py = sy * sy;
313         comp += DD.twoSquareLow(sy, py);
314         final double sumPy = sum + py;
315         comp += DD.twoSumLow(sum, py, sumPy);
316         sum = sumPy;
317 
318         return Math.sqrt(sum + comp) * rescale;
319     }
320 
321     /** Computes the Euclidean norm.
322      * This implementation handles possible overflow or underflow.
323      *
324      * @param x first input
325      * @param y second input
326      * @param z third input
327      * @return \(\sqrt{x^2 + y^2 + z^2}\)
328      *
329      * @see #L2
330      * @see #EUCLIDEAN
331      * @see #of(double,double,double)
332      */
333     private static double euclidean(final double x,
334                                     final double y,
335                                     final double z) {
336         final double xabs = Math.abs(x);
337         final double yabs = Math.abs(y);
338         final double zabs = Math.abs(z);
339 
340         final double max = Math.max(Math.max(xabs, yabs), zabs);
341 
342         // if the max is not finite, then one of the inputs must not have
343         // been finite
344         if (!Double.isFinite(max)) {
345             // let the standard multiply operation determine whether to
346             // return NaN or infinite
347             return xabs * yabs * zabs;
348         }
349 
350         // compute the scale and rescale values
351         final double scale;
352         final double rescale;
353         if (max > LARGE_THRESH) {
354             scale = SCALE_DOWN;
355             rescale = SCALE_UP;
356         } else if (max < SAFE_SCALE_UP_THRESH) {
357             scale = SCALE_UP;
358             rescale = SCALE_DOWN;
359         } else {
360             scale = 1d;
361             rescale = 1d;
362         }
363 
364 
365         // initialise sum and compensation using scaled x
366         final double sx = xabs * scale;
367         double sum = sx * sx;
368         double comp = DD.twoSquareLow(sx, sum);
369 
370         // add scaled y
371         final double sy = yabs * scale;
372         final double py = sy * sy;
373         comp += DD.twoSquareLow(sy, py);
374         final double sumPy = sum + py;
375         comp += DD.twoSumLow(sum, py, sumPy);
376         sum = sumPy;
377 
378         // add scaled z
379         final double sz = zabs * scale;
380         final double pz = sz * sz;
381         comp += DD.twoSquareLow(sz, pz);
382         final double sumPz = sum + pz;
383         comp += DD.twoSumLow(sum, pz, sumPz);
384         sum = sumPz;
385 
386         return Math.sqrt(sum + comp) * rescale;
387     }
388 
389     /** Computes the Euclidean norm.
390      * This implementation handles possible overflow or underflow.
391      *
392      * @param v input values
393      * @return \(\sqrt{v_0^2 + ... + v_{n-1}^2}\).
394      *
395      * @see #L2
396      * @see #EUCLIDEAN
397      * @see #of(double[])
398      */
399     private static double euclidean(final double[] v) {
400         // sum of big, normal and small numbers
401         double s1 = 0;
402         double s2 = 0;
403         double s3 = 0;
404 
405         // sum compensation values
406         double c1 = 0;
407         double c2 = 0;
408         double c3 = 0;
409 
410         for (int i = 0; i < v.length; ++i) {
411             final double x = Math.abs(v[i]);
412             if (!Double.isFinite(x)) {
413                 // not finite; determine whether to return NaN or positive infinity
414                 return euclideanNormSpecial(v, i);
415             } else if (x > LARGE_THRESH) {
416                 // scale down
417                 final double sx = x * SCALE_DOWN;
418 
419                 // compute the product and product compensation
420                 final double p = sx * sx;
421                 final double cp = DD.twoSquareLow(sx, p);
422 
423                 // compute the running sum and sum compensation
424                 final double s = s1 + p;
425                 final double cs = DD.twoSumLow(s1, p, s);
426 
427                 // update running totals
428                 c1 += cp + cs;
429                 s1 = s;
430             } else if (x < SMALL_THRESH) {
431                 // scale up
432                 final double sx = x * SCALE_UP;
433 
434                 // compute the product and product compensation
435                 final double p = sx * sx;
436                 final double cp = DD.twoSquareLow(sx, p);
437 
438                 // compute the running sum and sum compensation
439                 final double s = s3 + p;
440                 final double cs = DD.twoSumLow(s3, p, s);
441 
442                 // update running totals
443                 c3 += cp + cs;
444                 s3 = s;
445             } else {
446                 // no scaling
447                 // compute the product and product compensation
448                 final double p = x * x;
449                 final double cp = DD.twoSquareLow(x, p);
450 
451                 // compute the running sum and sum compensation
452                 final double s = s2 + p;
453                 final double cs = DD.twoSumLow(s2, p, s);
454 
455                 // update running totals
456                 c2 += cp + cs;
457                 s2 = s;
458             }
459         }
460 
461         // The highest sum is the significant component. Add the next significant.
462         // Note that the "x * SCALE_DOWN * SCALE_DOWN" expressions must be executed
463         // in the order given. If the two scale factors are multiplied together first,
464         // they will underflow to zero.
465         if (s1 != 0) {
466             // add s1, s2, c1, c2
467             final double s2Adj = s2 * SCALE_DOWN * SCALE_DOWN;
468             final double sum = s1 + s2Adj;
469             final double comp = DD.twoSumLow(s1, s2Adj, sum) +
470                 c1 + (c2 * SCALE_DOWN * SCALE_DOWN);
471             return Math.sqrt(sum + comp) * SCALE_UP;
472         } else if (s2 != 0) {
473             // add s2, s3, c2, c3
474             final double s3Adj = s3 * SCALE_DOWN * SCALE_DOWN;
475             final double sum = s2 + s3Adj;
476             final double comp = DD.twoSumLow(s2, s3Adj, sum) +
477                 c2 + (c3 * SCALE_DOWN * SCALE_DOWN);
478             return Math.sqrt(sum + comp);
479         }
480         // add s3, c3
481         return Math.sqrt(s3 + c3) * SCALE_DOWN;
482     }
483 
484     /** Special cases of non-finite input.
485      *
486      * @param v input vector
487      * @param start index to start examining the input vector from
488      * @return Euclidean norm special value
489      */
490     private static double euclideanNormSpecial(final double[] v,
491                                                final int start) {
492         for (int i = start; i < v.length; ++i) {
493             if (Double.isNaN(v[i])) {
494                 return Double.NaN;
495             }
496         }
497         return Double.POSITIVE_INFINITY;
498     }
499 
500     /** Computes the maximum norm.
501      *
502      * @param x first input
503      * @param y second input
504      * @return \(\max{(|x|, |y|)}\).
505      *
506      * @see #LINF
507      * @see #MAXIMUM
508      * @see #of(double,double)
509      */
510     private static double maximum(final double x,
511                                   final double y) {
512         return Math.max(Math.abs(x), Math.abs(y));
513     }
514 
515     /** Computes the maximum norm.
516      *
517      * @param x first input
518      * @param y second input
519      * @param z third input
520      * @return \(\max{(|x|, |y|, |z|)}\).
521      *
522      * @see #LINF
523      * @see #MAXIMUM
524      * @see #of(double,double,double)
525      */
526     private static double maximum(final double x,
527                                   final double y,
528                                   final double z) {
529         return Math.max(Math.abs(x),
530                         Math.max(Math.abs(y),
531                                  Math.abs(z)));
532     }
533 
534     /** Computes the maximum norm.
535      *
536      * @param v input values
537      * @return \(\max{(|v_0|, \ldots, |v_{n-1}|)}\)
538      *
539      * @see #LINF
540      * @see #MAXIMUM
541      * @see #of(double[])
542      */
543     private static double maximum(final double[] v) {
544         double max = 0d;
545         for (final double d : v) {
546             max = Math.max(max, Math.abs(d));
547         }
548         return max;
549     }
550 
551     /**
552      * @param a Array.
553      * @throws IllegalArgumentException for zero-size array.
554      */
555     private static void ensureNonEmpty(double[] a) {
556         if (a.length == 0) {
557             throw new IllegalArgumentException("Empty array");
558         }
559     }
560 }