blob: 48c3172a9d95c629a30e3e67ea9f09d4b9462d2b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.sis.referencing.operation.matrix;
import java.util.Random;
import org.apache.sis.test.DependsOn;
import org.apache.sis.test.TestUtilities;
import org.junit.AfterClass;
import org.junit.Test;
import static java.lang.Double.NaN;
import static org.opengis.test.Assert.*;
/**
* Tests the {@link NonSquareMatrix} implementation.
* This class inherits all tests defined in {@link MatrixTestCase}.
*
* <p>This class is expected to be the last {@code MatrixTestCase} subclass to be executed,
* because it sends the {@link #statistics} to {@link #out}. This condition is ensured if
* the tests are executed by {@link org.apache.sis.test.suite.ReferencingTestSuite}.
* However it is not a big deal if this condition is broken, as the only consequence
* is that reported statistics will be incomplete.</p>
*
* @author Martin Desruisseaux (Geomatys)
* @version 0.7
* @since 0.4
* @module
*/
@DependsOn(SolverTest.class)
public final strictfp class NonSquareMatrixTest extends MatrixTestCase {
/**
* Number of rows and columns, initialized by {@link #prepareNewMatrixSize(Random)}.
*/
private int numRow, numCol;
/**
* Computes a random size for the next matrix to create.
*
* @param random the random number generator to use.
*/
@Override
void prepareNewMatrixSize(final Random random) {
numRow = 5 + random.nextInt(8); // Matrix sizes from 5 to 12 inclusive.
int n;
do n = 5 + random.nextInt(8);
while (n == numRow);
numCol = n;
}
/** {@inheritDoc} */ @Override int getNumRow() {return numRow;}
/** {@inheritDoc} */ @Override int getNumCol() {return numCol;}
/**
* Ensures that the given matrix is an instance of the expected type.
*/
@Override
void validate(final MatrixSIS matrix) {
super.validate(matrix);
assertEquals(NonSquareMatrix.class, matrix.getClass());
}
/**
* Tests {@link NonSquareMatrix#inverse()} with a non-square matrix.
*
* @throws NoninvertibleMatrixException if the matrix can not be inverted.
*/
@Test
@Override
public void testInverse() throws NoninvertibleMatrixException {
testDimensionReduction(null, 1);
testDimensionIncrease (null, 1);
}
/**
* Tests inversion of a matrix with a column containing only a translation term.
* The purpose is to test the algorithm that selects the rows to omit.
*
* @throws NoninvertibleMatrixException if the matrix can not be inverted.
*/
@Test
public void testInverseWithTranslationTerm() throws NoninvertibleMatrixException {
final NonSquareMatrix m = new NonSquareMatrix(5, 3, new double[] {
2, 0, 0,
0, 0, 0,
0, 4, 0,
0, 0, 3,
0, 0, 1
});
MatrixSIS inverse = m.inverse();
assertMatrixEquals("Inverse of non-square matrix.", new NonSquareMatrix(3, 5, new double[] {
0.5, 0, 0, 0, 0,
0, 0, 0.25, 0, 0,
0, 0, 0, 0, 1}), inverse, STRICT);
assertMatrixEquals("Back to original.", new NonSquareMatrix(5, 3, new double[] {
2, 0, 0,
0, 0, NaN,
0, 4, 0,
0, 0, NaN,
0, 0, 1}), inverse.inverse(), STRICT);
/*
* Change the [0 0 3] row into [1 0 3]. The NonSquareMarix class should no longer omit that row.
* As a consequence, the matrix can not be inverted anymore.
*/
m.setElement(3, 0, 1);
try {
m.inverse();
fail("Matrix should not be invertible.");
} catch (NoninvertibleMatrixException e) {
assertNotNull(e.getMessage());
}
}
/**
* Tests {@link NonSquareMatrix#solve(Matrix)} with a non-square matrix.
*
* @throws NoninvertibleMatrixException if the matrix can not be inverted.
*/
@Test
@Override
public void testSolve() throws NoninvertibleMatrixException {
testDimensionReduction(new Matrix3(
2, 0, 0,
0, 2, 0,
0, 0, 1), 2);
testDimensionIncrease(new GeneralMatrix(5, 5, new double[] {
2, 0, 0, 0, 0,
0, 2, 0, 0, 0,
0, 0, 2, 0, 0,
0, 0, 0, 2, 0,
0, 0, 0, 0, 1}), 2);
}
/**
* Tests {@link NonSquareMatrix#inverse()} or {@link NonSquareMatrix#solve(Matrix)} with a conversion
* matrix having more source dimensions (columns) than target dimensions (rows).
*
* @param Y the matrix to give to {@code solve(Y)}, {@code null} for testing {@code inverse()}.
* @param sf the scale factor by which to multiply all expected scale elements.
* @throws NoninvertibleMatrixException if the matrix can not be inverted.
*/
private static void testDimensionReduction(final MatrixSIS Y, final double sf) throws NoninvertibleMatrixException {
final MatrixSIS matrix = Matrices.create(3, 5, new double[] {
2, 0, 0, 0, 8,
0, 0, 4, 0, 5,
0, 0, 0, 0, 1
});
final double[] expected = {
0.5*sf, 0, -4,
0, 0, NaN,
0, 0.25*sf, -1.25,
0, 0, NaN,
0, 0, 1
};
final MatrixSIS inverse = (Y != null) ? matrix.solve(Y) : matrix.inverse();
assertEqualsElements(expected, 5, 3, inverse, TOLERANCE);
}
/**
* Tests {@link NonSquareMatrix#inverse()} or {@link NonSquareMatrix#solve(Matrix)} with a conversion
* matrix having more target dimensions (rows) than source dimensions (columns).
*
* @param Y the matrix to give to {@code solve(Y)}, {@code null} for testing {@code inverse()}.
* @param sf the scale factor by which to multiply all expected scale elements.
* @throws NoninvertibleMatrixException if the matrix can not be inverted.
*/
private static void testDimensionIncrease(final MatrixSIS Y, final double sf)
throws NoninvertibleMatrixException
{
final MatrixSIS matrix = Matrices.create(5, 3, new double[] {
2, 0, 8,
NaN, NaN, NaN,
0, 4, 5,
0, 0, 0,
0, 0, 1
});
final double[] expected = {
0.5*sf, 0, 0, 0, -4,
0, 0, 0.25*sf, 0, -1.25,
0, 0, 0, 0, 1
};
final MatrixSIS inverse = (Y != null) ? matrix.solve(Y) : matrix.inverse();
assertEqualsElements(expected, 3, 5, inverse, TOLERANCE);
}
/**
* Prints the statistics about the differences between JAMA and SIS matrix elements.
* Those statistics will be visible only if {@link #VERBOSE} is {@code true}.
*/
@AfterClass
public static void printStatistics() {
if (statistics != null) {
TestUtilities.printSeparator("Overall statistics on agreement of matrix arithmetic");
synchronized (statistics) {
out.println(statistics);
}
TestUtilities.forceFlushOutput();
}
}
}