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    *      https://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  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.stat.inference;
23  
24  import org.hipparchus.distribution.continuous.ChiSquaredDistribution;
25  import org.hipparchus.exception.LocalizedCoreFormats;
26  import org.hipparchus.exception.MathIllegalArgumentException;
27  import org.hipparchus.exception.MathIllegalStateException;
28  import org.hipparchus.exception.NullArgumentException;
29  import org.hipparchus.stat.LocalizedStatFormats;
30  import org.hipparchus.util.FastMath;
31  import org.hipparchus.util.MathArrays;
32  import org.hipparchus.util.MathUtils;
33  
34  /**
35   * Implements Chi-Square test statistics.
36   * <p>
37   * This implementation handles both known and unknown distributions.
38   * <p>
39   * Two samples tests can be used when the distribution is unknown <i>a priori</i>
40   * but provided by one sample, or when the hypothesis under test is that the two
41   * samples come from the same underlying distribution.
42   */
43  public class ChiSquareTest { // NOPMD - this is not a Junit test class, PMD false positive here
44  
45      /** Empty constructor.
46       * <p>
47       * This constructor is not strictly necessary, but it prevents spurious
48       * javadoc warnings with JDK 18 and later.
49       * </p>
50       * @since 3.0
51       */
52      public ChiSquareTest() { // NOPMD - unnecessary constructor added intentionally to make javadoc happy
53          // nothing to do
54      }
55  
56      /**
57       * Computes the <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda35f.htm">
58       * Chi-Square statistic</a> comparing <code>observed</code> and <code>expected</code>
59       * frequency counts.
60       * <p>
61       * This statistic can be used to perform a Chi-Square test evaluating the null
62       * hypothesis that the observed counts follow the expected distribution.
63       * <p>
64       * <strong>Preconditions</strong>:
65       * <ul>
66       * <li>Expected counts must all be positive.</li>
67       * <li>Observed counts must all be &ge; 0.</li>
68       * <li>The observed and expected arrays must have the same length and
69       * their common length must be at least 2.</li>
70       * </ul>
71       * <p>
72       * If any of the preconditions are not met, an
73       * <code>IllegalArgumentException</code> is thrown.
74       * <p>
75       * <strong>Note: </strong>This implementation rescales the
76       * <code>expected</code> array if necessary to ensure that the sum of the
77       * expected and observed counts are equal.
78       *
79       * @param observed array of observed frequency counts
80       * @param expected array of expected frequency counts
81       * @return chiSquare test statistic
82       * @throws MathIllegalArgumentException if <code>observed</code> has negative entries
83       * @throws MathIllegalArgumentException if <code>expected</code> has entries that are
84       * not strictly positive
85       * @throws MathIllegalArgumentException if the arrays length is less than 2
86       */
87      public double chiSquare(final double[] expected, final long[] observed)
88          throws MathIllegalArgumentException {
89  
90          if (expected.length < 2) {
91              throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
92                                                     expected.length, 2);
93          }
94          MathUtils.checkDimension(expected.length, observed.length);
95          MathArrays.checkPositive(expected);
96          MathArrays.checkNonNegative(observed);
97  
98          double sumExpected = 0d;
99          double sumObserved = 0d;
100         for (int i = 0; i < observed.length; i++) {
101             sumExpected += expected[i];
102             sumObserved += observed[i];
103         }
104         double ratio = 1.0d;
105         boolean rescale = false;
106         if (FastMath.abs(sumExpected - sumObserved) > 10E-6) {
107             ratio = sumObserved / sumExpected;
108             rescale = true;
109         }
110         double sumSq = 0.0d;
111         for (int i = 0; i < observed.length; i++) {
112             if (rescale) {
113                 final double dev = observed[i] - ratio * expected[i];
114                 sumSq += dev * dev / (ratio * expected[i]);
115             } else {
116                 final double dev = observed[i] - expected[i];
117                 sumSq += dev * dev / expected[i];
118             }
119         }
120         return sumSq;
121     }
122 
123     /**
124      * Returns the <i>observed significance level</i>, or <a href=
125      * "http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">
126      * p-value</a>, associated with a
127      * <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda35f.htm">
128      * Chi-square goodness of fit test</a> comparing the <code>observed</code>
129      * frequency counts to those in the <code>expected</code> array.
130      * <p>
131      * The number returned is the smallest significance level at which one can reject
132      * the null hypothesis that the observed counts conform to the frequency distribution
133      * described by the expected counts.
134      * <p>
135      * <strong>Preconditions</strong>:
136      * <ul>
137      * <li>Expected counts must all be positive.</li>
138      * <li>Observed counts must all be &ge; 0.</li>
139      * <li>The observed and expected arrays must have the same length and
140      * their common length must be at least 2.</li>
141      * </ul>
142      * <p>
143      * If any of the preconditions are not met, an
144      * <code>IllegalArgumentException</code> is thrown.
145      * <p>
146      * <strong>Note: </strong>This implementation rescales the
147      * <code>expected</code> array if necessary to ensure that the sum of the
148      * expected and observed counts are equal.
149      *
150      * @param observed array of observed frequency counts
151      * @param expected array of expected frequency counts
152      * @return p-value
153      * @throws MathIllegalArgumentException if <code>observed</code> has negative entries
154      * @throws MathIllegalArgumentException if <code>expected</code> has entries that are
155      * not strictly positive
156      * @throws MathIllegalArgumentException if the arrays length is less than 2
157      * @throws MathIllegalStateException if an error occurs computing the p-value
158      */
159     public double chiSquareTest(final double[] expected, final long[] observed)
160         throws MathIllegalArgumentException, MathIllegalStateException {
161 
162         final ChiSquaredDistribution distribution = new ChiSquaredDistribution(expected.length - 1.0);
163         return 1.0 - distribution.cumulativeProbability(chiSquare(expected, observed));
164     }
165 
166     /**
167      * Performs a <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda35f.htm">
168      * Chi-square goodness of fit test</a> evaluating the null hypothesis that the
169      * observed counts conform to the frequency distribution described by the expected
170      * counts, with significance level <code>alpha</code>.  Returns true iff the null
171      * hypothesis can be rejected with 100 * (1 - alpha) percent confidence.
172      * <p>
173      * <strong>Example:</strong><br>
174      * To test the hypothesis that <code>observed</code> follows
175      * <code>expected</code> at the 99% level, use
176      * <code>chiSquareTest(expected, observed, 0.01)</code>
177      * <p>
178      * <strong>Preconditions</strong>:
179      * <ul>
180      * <li>Expected counts must all be positive.</li>
181      * <li>Observed counts must all be &ge; 0.</li>
182      * <li>The observed and expected arrays must have the same length and
183      * their common length must be at least 2.</li>
184      * <li><code> 0 &lt; alpha &lt; 0.5</code></li>
185      * </ul>
186      * <p>
187      * If any of the preconditions are not met, an
188      * <code>IllegalArgumentException</code> is thrown.
189      * <p>
190      * <strong>Note: </strong>This implementation rescales the
191      * <code>expected</code> array if necessary to ensure that the sum of the
192      * expected and observed counts are equal.
193      *
194      * @param observed array of observed frequency counts
195      * @param expected array of expected frequency counts
196      * @param alpha significance level of the test
197      * @return true iff null hypothesis can be rejected with confidence
198      * 1 - alpha
199      * @throws MathIllegalArgumentException if <code>observed</code> has negative entries
200      * @throws MathIllegalArgumentException if <code>expected</code> has entries that are
201      * not strictly positive
202      * @throws MathIllegalArgumentException if the arrays length is less than 2
203      * @throws MathIllegalArgumentException if <code>alpha</code> is not in the range (0, 0.5]
204      * @throws MathIllegalStateException if an error occurs computing the p-value
205      */
206     public boolean chiSquareTest(final double[] expected, final long[] observed,
207                                  final double alpha)
208         throws MathIllegalArgumentException, MathIllegalStateException {
209 
210         if ((alpha <= 0) || (alpha > 0.5)) {
211             throw new MathIllegalArgumentException(LocalizedStatFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
212                                           alpha, 0, 0.5);
213         }
214         return chiSquareTest(expected, observed) < alpha;
215 
216     }
217 
218     /**
219      * Computes the Chi-Square statistic associated with a
220      * <a href="http://www.itl.nist.gov/div898/handbook/prc/section4/prc45.htm">
221      * chi-square test of independence</a> based on the input <code>counts</code>
222      * array, viewed as a two-way table.
223      * <p>
224      * The rows of the 2-way table are
225      * <code>count[0], ... , count[count.length - 1] </code>
226      * <p>
227      * <strong>Preconditions</strong>:
228      * <ul>
229      * <li>All counts must be &ge; 0.</li>
230      * <li>The count array must be rectangular (i.e. all count[i] subarrays
231      * must have the same length).</li>
232      * <li>The 2-way table represented by <code>counts</code> must have at
233      * least 2 columns and at least 2 rows.</li>
234      * </ul>
235      * <p>
236      * If any of the preconditions are not met, an
237      * <code>IllegalArgumentException</code> is thrown.
238      *
239      * @param counts array representation of 2-way table
240      * @return chiSquare test statistic
241      * @throws NullArgumentException if the array is null
242      * @throws MathIllegalArgumentException if the array is not rectangular
243      * @throws MathIllegalArgumentException if {@code counts} has negative entries
244      */
245     public double chiSquare(final long[][] counts)
246         throws MathIllegalArgumentException, NullArgumentException {
247 
248         checkArray(counts);
249         int nRows = counts.length;
250         int nCols = counts[0].length;
251 
252         // compute row, column and total sums
253         double[] rowSum = new double[nRows];
254         double[] colSum = new double[nCols];
255         double total = 0.0d;
256         for (int row = 0; row < nRows; row++) {
257             for (int col = 0; col < nCols; col++) {
258                 rowSum[row] += counts[row][col];
259                 colSum[col] += counts[row][col];
260                 total += counts[row][col];
261             }
262         }
263 
264         // compute expected counts and chi-square
265         double sumSq = 0.0d;
266         for (int row = 0; row < nRows; row++) {
267             for (int col = 0; col < nCols; col++) {
268                 final double expected = (rowSum[row] * colSum[col]) / total;
269                 sumSq += ((counts[row][col] - expected) *
270                         (counts[row][col] - expected)) / expected;
271             }
272         }
273         return sumSq;
274     }
275 
276     /**
277      * Returns the <i>observed significance level</i>, or <a href=
278      * "http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">
279      * p-value</a>, associated with a
280      * <a href="http://www.itl.nist.gov/div898/handbook/prc/section4/prc45.htm">
281      * chi-square test of independence</a> based on the input <code>counts</code>
282      * array, viewed as a two-way table.
283      * <p>
284      * The rows of the 2-way table are
285      * <code>count[0], ... , count[count.length - 1] </code>
286      * <p>
287      * <strong>Preconditions</strong>:
288      * <ul>
289      * <li>All counts must be &ge; 0.</li>
290      * <li>The count array must be rectangular (i.e. all count[i] subarrays must have
291      * the same length).</li>
292      * <li>The 2-way table represented by <code>counts</code> must have at least 2
293      * columns and at least 2 rows.</li>
294      * </ul>
295      * <p>
296      * If any of the preconditions are not met, an
297      * <code>IllegalArgumentException</code> is thrown.
298      *
299      * @param counts array representation of 2-way table
300      * @return p-value
301      * @throws NullArgumentException if the array is null
302      * @throws MathIllegalArgumentException if the array is not rectangular
303      * @throws MathIllegalArgumentException if {@code counts} has negative entries
304      * @throws MathIllegalStateException if an error occurs computing the p-value
305      */
306     public double chiSquareTest(final long[][] counts)
307         throws MathIllegalArgumentException, NullArgumentException, MathIllegalStateException {
308 
309         checkArray(counts);
310         double df = ((double) counts.length -1) * ((double) counts[0].length - 1);
311         final ChiSquaredDistribution distribution = new ChiSquaredDistribution(df);
312         return 1 - distribution.cumulativeProbability(chiSquare(counts));
313     }
314 
315     /**
316      * Performs a <a href="http://www.itl.nist.gov/div898/handbook/prc/section4/prc45.htm">
317      * chi-square test of independence</a> evaluating the null hypothesis that the
318      * classifications represented by the counts in the columns of the input 2-way table
319      * are independent of the rows, with significance level <code>alpha</code>.
320      * Returns true iff the null hypothesis can be rejected with 100 * (1 - alpha) percent
321      * confidence.
322      * <p>
323      * The rows of the 2-way table are
324      * <code>count[0], ... , count[count.length - 1] </code>
325      * <p>
326      * <strong>Example:</strong><br>
327      * To test the null hypothesis that the counts in
328      * <code>count[0], ... , count[count.length - 1] </code>
329      * all correspond to the same underlying probability distribution at the 99% level,
330      * use <code>chiSquareTest(counts, 0.01)</code>.
331      * <p>
332      * <strong>Preconditions</strong>:
333      * <ul>
334      * <li>All counts must be &ge; 0.</li>
335      * <li>The count array must be rectangular (i.e. all count[i] subarrays must have the
336      * same length).</li>
337      * <li>The 2-way table represented by <code>counts</code> must have at least 2 columns and
338      * at least 2 rows.</li>
339      * </ul>
340      * <p>
341      * If any of the preconditions are not met, an
342      * <code>IllegalArgumentException</code> is thrown.
343      *
344      * @param counts array representation of 2-way table
345      * @param alpha significance level of the test
346      * @return true iff null hypothesis can be rejected with confidence
347      * 1 - alpha
348      * @throws NullArgumentException if the array is null
349      * @throws MathIllegalArgumentException if the array is not rectangular
350      * @throws MathIllegalArgumentException if {@code counts} has any negative entries
351      * @throws MathIllegalArgumentException if <code>alpha</code> is not in the range (0, 0.5]
352      * @throws MathIllegalStateException if an error occurs computing the p-value
353      */
354     public boolean chiSquareTest(final long[][] counts, final double alpha)
355         throws MathIllegalArgumentException, NullArgumentException, MathIllegalStateException {
356 
357         if ((alpha <= 0) || (alpha > 0.5)) {
358             throw new MathIllegalArgumentException(LocalizedStatFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
359                                           alpha, 0, 0.5);
360         }
361         return chiSquareTest(counts) < alpha;
362     }
363 
364     /**
365      * Computes a
366      * <a href="http://www.itl.nist.gov/div898/software/dataplot/refman1/auxillar/chi2samp.htm">
367      * Chi-Square two sample test statistic</a> comparing bin frequency counts
368      * in <code>observed1</code> and <code>observed2</code>.
369      * <p>
370      * The sums of frequency counts in the two samples are not required to be the
371      * same. The formula used to compute the test statistic is
372      * </p>
373      * <code>
374      * &sum;[(K * observed1[i] - observed2[i]/K)<sup>2</sup> / (observed1[i] + observed2[i])]
375      * </code>
376      * <p>
377      * where
378      * </p>
379      * <code>K = √[&sum;(observed2 / &sum;(observed1)]</code>
380      * <p>
381      * This statistic can be used to perform a Chi-Square test evaluating the
382      * null hypothesis that both observed counts follow the same distribution.
383      * </p>
384      * <p><strong>Preconditions</strong>:</p>
385      * <ul>
386      * <li>Observed counts must be non-negative.</li>
387      * <li>Observed counts for a specific bin must not both be zero.</li>
388      * <li>Observed counts for a specific sample must not all be 0.</li>
389      * <li>The arrays <code>observed1</code> and <code>observed2</code> must have
390      * the same length and their common length must be at least 2.</li>
391      * </ul>
392      * <p>
393      * If any of the preconditions are not met, an
394      * <code>IllegalArgumentException</code> is thrown.
395      * </p>
396      *
397      * @param observed1 array of observed frequency counts of the first data set
398      * @param observed2 array of observed frequency counts of the second data set
399      * @return chiSquare test statistic
400      * @throws MathIllegalArgumentException the the length of the arrays does not match
401      * @throws MathIllegalArgumentException if any entries in <code>observed1</code> or
402      * <code>observed2</code> are negative
403      * @throws MathIllegalArgumentException if either all counts of <code>observed1</code> or
404      * <code>observed2</code> are zero, or if the count at some index is zero
405      * for both arrays
406      */
407     public double chiSquareDataSetsComparison(long[] observed1, long[] observed2)
408         throws MathIllegalArgumentException {
409 
410         // Make sure lengths are same
411         if (observed1.length < 2) {
412             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
413                                                    observed1.length, 2);
414         }
415         MathUtils.checkDimension(observed1.length, observed2.length);
416 
417         // Ensure non-negative counts
418         MathArrays.checkNonNegative(observed1);
419         MathArrays.checkNonNegative(observed2);
420 
421         // Compute and compare count sums
422         long countSum1 = 0;
423         long countSum2 = 0;
424         for (int i = 0; i < observed1.length; i++) {
425             countSum1 += observed1[i];
426             countSum2 += observed2[i];
427         }
428         // Ensure neither sample is uniformly 0
429         if (countSum1 == 0 || countSum2 == 0) {
430             throw new MathIllegalArgumentException(LocalizedCoreFormats.ZERO_NOT_ALLOWED);
431         }
432         // Compare and compute weight only if different
433         double weight = 0.0;
434         boolean unequalCounts = countSum1 != countSum2;
435         if (unequalCounts) {
436             weight = FastMath.sqrt((double) countSum1 / (double) countSum2);
437         }
438         // Compute ChiSquare statistic
439         double sumSq = 0.0d;
440         for (int i = 0; i < observed1.length; i++) {
441             if (observed1[i] == 0 && observed2[i] == 0) {
442                 throw new MathIllegalArgumentException(LocalizedCoreFormats.OBSERVED_COUNTS_BOTTH_ZERO_FOR_ENTRY, i);
443             } else {
444                 final double obs1 = observed1[i];
445                 final double obs2 = observed2[i];
446                 final double dev;
447                 if (unequalCounts) { // apply weights
448                     dev = obs1/weight - obs2 * weight;
449                 } else {
450                     dev = obs1 - obs2;
451                 }
452                 sumSq += (dev * dev) / (obs1 + obs2);
453             }
454         }
455         return sumSq;
456     }
457 
458     /**
459      * Returns the <i>observed significance level</i>, or <a href=
460      * "http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">
461      * p-value</a>, associated with a Chi-Square two sample test comparing
462      * bin frequency counts in <code>observed1</code> and
463      * <code>observed2</code>.
464      * <p>
465      * The number returned is the smallest significance level at which one
466      * can reject the null hypothesis that the observed counts conform to the
467      * same distribution.
468      * <p>
469      * See {@link #chiSquareDataSetsComparison(long[], long[])} for details
470      * on the formula used to compute the test statistic. The degrees of
471      * of freedom used to perform the test is one less than the common length
472      * of the input observed count arrays.
473      * <p>
474      * <strong>Preconditions</strong>:
475      * <ul>
476      * <li>Observed counts must be non-negative.</li>
477      * <li>Observed counts for a specific bin must not both be zero.</li>
478      * <li>Observed counts for a specific sample must not all be 0.</li>
479      * <li>The arrays <code>observed1</code> and <code>observed2</code> must
480      * have the same length and their common length must be at least 2.</li>
481      * </ul>
482      * <p>
483      * If any of the preconditions are not met, an
484      * <code>IllegalArgumentException</code> is thrown.
485      *
486      * @param observed1 array of observed frequency counts of the first data set
487      * @param observed2 array of observed frequency counts of the second data set
488      * @return p-value
489      * @throws MathIllegalArgumentException the the length of the arrays does not match
490      * @throws MathIllegalArgumentException if any entries in <code>observed1</code> or
491      * <code>observed2</code> are negative
492      * @throws MathIllegalArgumentException if either all counts of <code>observed1</code> or
493      * <code>observed2</code> are zero, or if the count at the same index is zero
494      * for both arrays
495      * @throws MathIllegalStateException if an error occurs computing the p-value
496      */
497     public double chiSquareTestDataSetsComparison(long[] observed1, long[] observed2)
498         throws MathIllegalArgumentException,
499         MathIllegalStateException {
500 
501         final ChiSquaredDistribution distribution =
502                 new ChiSquaredDistribution((double) observed1.length - 1);
503         return 1 - distribution.cumulativeProbability(
504                 chiSquareDataSetsComparison(observed1, observed2));
505     }
506 
507     /**
508      * Performs a Chi-Square two sample test comparing two binned data
509      * sets. The test evaluates the null hypothesis that the two lists of
510      * observed counts conform to the same frequency distribution, with
511      * significance level <code>alpha</code>.  Returns true iff the null
512      * hypothesis can be rejected with 100 * (1 - alpha) percent confidence.
513      * <p>
514      * See {@link #chiSquareDataSetsComparison(long[], long[])} for
515      * details on the formula used to compute the Chisquare statistic used
516      * in the test. The degrees of of freedom used to perform the test is
517      * one less than the common length of the input observed count arrays.
518      * <p>
519      * <strong>Preconditions</strong>:
520      * <ul>
521      * <li>Observed counts must be non-negative.</li>
522      * <li>Observed counts for a specific bin must not both be zero.</li>
523      * <li>Observed counts for a specific sample must not all be 0.</li>
524      * <li>The arrays <code>observed1</code> and <code>observed2</code> must
525      * have the same length and their common length must be at least 2.</li>
526      * <li><code> 0 &lt; alpha &lt; 0.5</code></li>
527      * </ul>
528      * <p>
529      * If any of the preconditions are not met, an
530      * <code>IllegalArgumentException</code> is thrown.
531      *
532      * @param observed1 array of observed frequency counts of the first data set
533      * @param observed2 array of observed frequency counts of the second data set
534      * @param alpha significance level of the test
535      * @return true iff null hypothesis can be rejected with confidence
536      * 1 - alpha
537      * @throws MathIllegalArgumentException the the length of the arrays does not match
538      * @throws MathIllegalArgumentException if any entries in <code>observed1</code> or
539      * <code>observed2</code> are negative
540      * @throws MathIllegalArgumentException if either all counts of <code>observed1</code> or
541      * <code>observed2</code> are zero, or if the count at the same index is zero
542      * for both arrays
543      * @throws MathIllegalArgumentException if <code>alpha</code> is not in the range (0, 0.5]
544      * @throws MathIllegalStateException if an error occurs performing the test
545      */
546     public boolean chiSquareTestDataSetsComparison(final long[] observed1,
547                                                    final long[] observed2,
548                                                    final double alpha)
549         throws MathIllegalArgumentException, MathIllegalStateException {
550 
551         if (alpha <= 0 ||
552             alpha > 0.5) {
553             throw new MathIllegalArgumentException(LocalizedStatFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
554                                           alpha, 0, 0.5);
555         }
556         return chiSquareTestDataSetsComparison(observed1, observed2) < alpha;
557 
558     }
559 
560     /**
561      * Checks to make sure that the input long[][] array is rectangular,
562      * has at least 2 rows and 2 columns, and has all non-negative entries.
563      *
564      * @param in input 2-way table to check
565      * @throws NullArgumentException if the array is null
566      * @throws MathIllegalArgumentException if the array is not valid
567      * @throws MathIllegalArgumentException if the array contains any negative entries
568      */
569     private void checkArray(final long[][] in)
570         throws MathIllegalArgumentException, NullArgumentException {
571 
572         if (in.length < 2) {
573             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
574                                                    in.length, 2);
575         }
576 
577         if (in[0].length < 2) {
578             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
579                                                    in[0].length, 2);
580         }
581 
582         MathArrays.checkRectangular(in);
583         MathArrays.checkNonNegative(in);
584     }
585 
586 }