All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.algart.math.functions.LinearOperator Maven / Gradle / Ivy

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2007-2024 Daniel Alievsky, AlgART Laboratory (http://algart.net)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package net.algart.math.functions;

import net.algart.math.Point;

import java.util.Objects;

/**
 * 

Linear operator (affine transformation): * O f(x) = f(Ax + b), * where the numeric n x n matrix A * and the n-dimensional vector b are parameters of the transformation. * (It is a particular case of the {@link ProjectiveOperator projective transformation}, * when c vector is zero and d number is 1.0.)

* *

This class is immutable and thread-safe: * there are no ways to modify settings of the created instance.

* * @author Daniel Alievsky */ public final class LinearOperator extends ProjectiveOperator { LinearOperator(double[] a, double[] diagonal, double[] b) { super(a, diagonal, b, null, 1.0); } /** * Returns an instance of this class, describing the linear operator with the specified matrix A * and vector b: * O f(x) = f(Ax + b). * The coordinates of the vector b must be listed in b array. * The elements of the matrix A must be listed, row by row, in the a array: * A={aij}, * aij=a[i*n+j], * i is the index of the row (0..n-1), * j is the index of the column (0..n-1), * n=b.length. * The length a.length of the a array must be equal to the square n2 * of the length n=b.length of the b array. * Empty arrays (n=0) are not allowed. * *

The passed a and b Java arrays are cloned by this method: no references to them * are maintained by the created instance. * * @param a the elements of A matrix. * @param b the coordinates of b vector. * @return the linear operator O f(x) = f(Ax + b). * @throws NullPointerException if one of the arguments of the method is {@code null}. * @throws IllegalArgumentException if b.length==0 or a.length!=b.length2. */ public static LinearOperator getInstance(double[] a, double[] b) { Objects.requireNonNull(a, "Null A matrix"); Objects.requireNonNull(b, "Null b vector"); return new LinearOperator(a.clone(), null, b.clone()); } /** * Returns an instance of this class, describing the linear operator with the diagonal matrix A * and vector b: * O f(x) = f(Ax + b), * where A={aij}, * aij=0.0 if i!=j, * aii=diagonal[i]. * The coordinates of the vector b must be listed in b array. * Empty arrays (diagonal.length=b.length=0) are not allowed. * *

This linear operator performs resizing and shift along coordinate axes. * *

The passed diagonal and b Java arrays are cloned by this method: no references to them * are maintained by the created instance. * * @param diagonal the diagonal elements of A matrix (all other elements are supposed to be zero). * @param b the coordinates of b vector. * @return the linear operator O f(x) = f(Ax + b). * @throws NullPointerException if one of the arguments of the method is {@code null}. * @throws IllegalArgumentException if diagonal.length==0 or diagonal.length!=b.length. */ public static LinearOperator getDiagonalInstance(double[] diagonal, double[] b) { Objects.requireNonNull(diagonal, "Null diagonal array"); Objects.requireNonNull(b, "Null b vector"); return new LinearOperator(null, diagonal.clone(), b.clone()); } /** * Equivalent to * {@link #getDiagonalInstance(double[], double[]) getDiagonalInstance(diagonal, new double[diagonal.length])} * (the case of zero b vector). * This linear operator performs resizing along coordinate axes. * * @param diagonal the diagonal elements of A matrix (all other elements are supposed to be zero). * @return the linear operator O f(x) = f(Ax). * @throws NullPointerException if the argument of the method is {@code null}. * @throws IllegalArgumentException if diagonal.length==0. */ public static LinearOperator getDiagonalInstance(double... diagonal) { Objects.requireNonNull(diagonal, "Null diagonal array"); return new LinearOperator(null, diagonal.clone(), new double[diagonal.length]); } /** * Equivalent to * {@link #getDiagonalInstance(double[], double[]) getDiagonalInstance(diagonal, b)}, * where diagonal is an array consisting of b.length unit values (1.0). * This linear operator performs shifting along coordinate axes. * * @param b the coordinates of b vector. * @return the linear operator O f(x) = f(x + b). * @throws NullPointerException if the argument of the method is {@code null}. * @throws IllegalArgumentException if b.length==0. */ public static LinearOperator getShiftInstance(double... b) { Objects.requireNonNull(b, "Null b array"); return new LinearOperator(null, null, b); } /** * Returns an instance of this class, describing rotation in 2D plane by the specified angle (in radians) * around the specified center. * Almost equivalent to {@link #getInstance(double[], double[]) getInstance}(a,b), * where: *

    *
  • a = {cos,sin,-sin,cos} — matrix A * (cos=StrictMath.cos(angle)), * sin=StrictMath.sin(angle));
  • * *
  • b = {centerX-a[0]*centerX-a[1]*centerY, * centerY-a[2]*centerX-a[3]*centerY} — vector * b=cAc (c={centerX, centerY}).
  • *
* *

The only difference from these formulas is special processing some cases, when the angle is kπ/2 * with integer k (more precisely, k/2.0*StrictMath.PI): * StrictMath.cos and StrictMath.sin methods can return inexact results here, * but this method uses precise values ±1 in these cases. * * @param centerX the x-coordinate of the rotation center. * @param centerY the y-coordinate of the rotation center. * @param angle the rotation angle (in radians; positive values correspond to clockwise rotation, * if the x axis is directed rightwards and the y axis * is directed downwards, according traditions of computer image processing). * @return 2-dimensional linear operator, describing this rotation. */ public static LinearOperator getRotation2D(double centerX, double centerY, double angle) { double cos = StrictMath.cos(angle); double sin = StrictMath.sin(angle); for (int k = -4; k <= 4; k++) { if (angle == k / 2.0 * StrictMath.PI) { // use precise cosine and sine in these cases assert StrictMath.abs(StrictMath.round(cos) - cos) < 1e-6; assert StrictMath.abs(StrictMath.round(sin) - sin) < 1e-6; cos = StrictMath.round(cos); sin = StrictMath.round(sin); } } // y=A(x-c)+c=Ax+c-Ac double[] a = { cos, sin, -sin, cos, }; double[] b = { centerX - a[0] * centerX - a[1] * centerY, centerY - a[2] * centerX - a[3] * centerY, }; return new LinearOperator(a, null, b); } /** * Returns the n-dimensional linear operator, that transforms (maps) * the given n+1 points p0, p1, ..., pn to * the given another n+1 points q0, q1, ..., qn * of the n-dimensional space. * In other words, the matrix A and the vector b in the returned operator * fulfil the following n+1 conditions: * *

* Ap0 + b = q0,
* Ap1 + b = q1,
* ...,
* Apn + b = qn *
* *

It is possible that there is no such operator * or there are many different solutions (degenerated cases). * In this case, this method still returns some operator, but some coefficients of A matrix * and b vector in the returned operator will probably be Double.NaN, * Double.POSITIVE_INFINITY or Double.NEGATIVE_INFINITY. * *

All passed points must be n-dimensional, * where n+1=p.length=q.length. * * @param q the destination points. * @param p the source points. * @return the n-dimensional linear operator, which maps pi to * qi for all i=0,1,2,...,n. * @throws NullPointerException if one of arguments of this method or one of elements of * p and q arrays {@code null}. * @throws IllegalArgumentException if the lengths of the passed p and * q arrays are not equal, * or if for some k * p[k].{@link Point#coordCount() coordCount()}!=p.length-1 or * q[k].{@link Point#coordCount() coordCount()}!=p.length-1. * @throws OutOfMemoryError if there is not enough Java memory for storing two Java arrays * double[n*n] and double[(n+1)*(n+1)], * where n+1=p.length, * or if (n+1)*(n+1)>Integer.MAX_VALUE. */ public static LinearOperator getInstanceByPoints(Point[] q, Point[] p) { Objects.requireNonNull(p, "Null p argument"); Objects.requireNonNull(q, "Null q argument"); if (p.length != q.length) throw new IllegalArgumentException("p and q point arrays lengths mismatch: p.length=" + p.length + ", q.length=" + q.length); if (p.length == 0) throw new IllegalArgumentException("Empty p and q arrays"); final int n = p.length - 1; if (((long) n + 1) * ((long) n + 1) > Integer.MAX_VALUE) throw new OutOfMemoryError("Too large necessary matrix (more than Integer.MAX_VALUE elements)"); for (int k = 0; k < p.length; k++) { if (p[k].coordCount() != n) throw new IllegalArgumentException("n+1 n-dimensional points are necessary to " + "find the linear operator, but we have " + (n + 1) + " points, " + "and the source point #" + k + " is " + p[k].coordCount() + "-dimensional"); if (q[k].coordCount() != n) throw new IllegalArgumentException("n+1 n-dimensional points are necessary to " + "find the linear operator, but we have " + (n + 1) + " points, " + "and the destination point #" + k + " is " + q[k].coordCount() + "-dimensional"); } // A * (px0,py0,pz0,...) + b = (qx0,qy0,qz0,...) // A * (px1,py1,pz1,...) + b = (qx1,qy1,qz1,...) // ..., i.e. // // px0*a00 + py0*a01 + pz0*a02 + ... + bx = qx0 // px1*a00 + py1*a01 + pz1*a02 + ... + bx = qx1 // px2*a00 + py2*a01 + pz2*a02 + ... + bx = qx2 // ... // px0*a10 + py0*a11 + pz0*a12 + ... + by = qy0 // px1*a10 + py1*a11 + pz1*a12 + ... + by = qy1 // px2*a10 + py2*a11 + pz2*a13 + ... + by = qy2 // etc. // In other words, here are n independent equation systems Sv=t double[] s = new double[(n + 1) * (n + 1)]; double[] t = new double[n + 1]; double[] v = new double[n + 1]; for (int i = 0, sOfs = 0; i <= n; i++) { for (int j = 0; j < n; j++, sOfs++) { s[sOfs] = p[i].coord(j); } s[sOfs++] = 1.0; } // S is the matrix of the equations set (same for all equations sets) double[] a = new double[n * n]; double[] b = new double[n]; for (int i = 0, aOfs = 0; i < n; i++, aOfs += n) { // we shall solve here equations set #i // and find the line #i of the matrix A and the element #i of the vector b for (int j = 0; j <= n; j++) { t[j] = q[j].coord(i); } // t is the right side of the equations set solveLinearEquationsSet(v, s.clone(), t); // solveLinearEquationsSetByCramerMethod(v, s, t); System.arraycopy(v, 0, a, aOfs, n); b[i] = v[n]; } return new LinearOperator(a, null, b); } /** * Returns superposition of this and the passed linear operators. * More precisely, if this operator corresponds to the affine transformation Ax + b, * and the passed one corresponds to the affine transformation A'x + b', * then the returned operator corresponds to the affine transformation * A''x + b'' = A'(Ax + b) + b', * i.e. in the returned operator * A'' = A'A, b'' = A'b + b'. * * @param operator the second operator, that should be applied after this one. * @return superposition of this and the passed operator. * @throws NullPointerException if the argument of the method is {@code null}. * @throws IllegalArgumentException if operator.{@link #n() n()}!=this.{@link #n() n()}. */ public LinearOperator superposition(LinearOperator operator) { Objects.requireNonNull(operator, "Null operator argument"); if (operator.n != n) throw new IllegalArgumentException("Passed and this operators dimensions mismatch: operator.n()=" + operator.n + ", this.n()=" + n); if (operator.isShift()) { double[] b = this.b.clone(); for (int i = 0; i < n; i++) { b[i] += operator.b[i]; } return new LinearOperator(this.a, this.diagonal, b); } double[] a = this.a != null ? this.a : this.a(); // A double[] aOther = operator.a != null ? operator.a : operator.a(); // A' double[] aNew = new double[a.length]; // A'' (result) double[] bNew = new double[n]; // b'' (result) // y'' = A'y' + b' = A'(Ax+b) + b' = A'Ax + (A'b+b'), so A''=A'A, b''=A'b+b' for (int in = 0; in < a.length; in += n) { // in = i * n for (int j = 0; j < n; j++) { double sum = 0.0; for (int k = 0, disp = j; k < n; k++, disp += n) { sum += aOther[in + k] * a[disp]; // aOther[i * n + k] * a[k * n + j]; } aNew[in + j] = sum; } } for (int i = 0, in = 0; i < n; i++, in += n) { // in = i * n double sum = operator.b[i]; for (int j = 0; j < n; j++) { sum += aOther[in + j] * b[j]; } bNew[i] = sum; } return new LinearOperator(aNew, null, bNew); } /** * Returns an instance of this class, identical to this one execpting that the new instance has the specified * vector b. * *

The passed b Java array is cloned by this method: no references to it * are maintained by the created instance. * * @param b the new coordinates of b vector. * @return the linear operator with changed b vector. * @throws NullPointerException if the argument of the method is {@code null}. * @throws IllegalArgumentException if b.length!=this.{@link #n() n()}. */ public LinearOperator changeB(double... b) { Objects.requireNonNull(b, "Null b array"); if (b.length != n) throw new IllegalArgumentException("Passed b and this.b vector lengths mismatch: b.length=" + b.length + ", this b.length=" + n); return new LinearOperator(this.a, this.diagonal, b); } /** * This implementation calculates destPoint by multiplication * the srcPoint by the matrix A and adding the vector b. * to the coordinates destPoint of the destination point. * * @param destPoint the coordinates of the destination point y, filled by this method. * @param srcPoint the coordinates of the source point x. * @throws NullPointerException if one of the arguments is {@code null}. * @throws IllegalArgumentException if destPoint.length or srcPoint.length * is not equal to the {@link #n() number of dimensions}. */ public void map(double[] destPoint, double[] srcPoint) { calculateAxPlusB(destPoint, srcPoint); } /** * Transforms the coordinates destPoint of the destination point in n-dimensional space * back to the coordinates srcPoint of the original point. * *

To find the srcPoint, this method solves the system of linear equations Ax=y−b, * where the matrix A and the vector b are the parameters of this transformation, * y is destPoint, x is srcPoint. * This method uses * Gauss elimination algorithm * with partial (column) pivoting. * This algorithm requires O(n3) operations. * *

It is possible that there is no required srcPoint * or there are many different solutions (degenerated cases). * In this case, this method still returns some point, but some found srcPoint coordinates * will probably be Double.NaN, Double.POSITIVE_INFINITY * or Double.NEGATIVE_INFINITY. * *

Note: this method allocates some additional memory if the matrix A * is not {@link #isDiagonal() diagonal}. * If you don't want to occupy additional memory, you can directly use * {@link #solveLinearEquationsSet(double[], double[], double[])} method. * *

Note: this method works correctly even if destPoint and srcPoint * is the same Java array. * * @param srcPoint the coordinates of the source point x, filled by this method. * @param destPoint the coordinates of the destinated point y. * @throws NullPointerException if one of the arguments is {@code null}. * @throws IllegalArgumentException if destPoint.length or srcPoint.length * is not equal to the {@link #n() number of dimensions}. */ public void inverseMap(double[] srcPoint, double[] destPoint) { Objects.requireNonNull(srcPoint, "Null srcPoint"); Objects.requireNonNull(destPoint, "Null destPoint"); if (srcPoint.length != n) throw new IllegalArgumentException("Illegal length of srcPoint array: " + srcPoint.length + " for " + this); if (destPoint.length != n) throw new IllegalArgumentException("Illegal length of destPoint array: " + destPoint.length + " for " + this); if (a != null) { double[] yMinusB = new double[n]; for (int i = 0; i < n; i++) { yMinusB[i] = destPoint[i] - b[i]; } double[] aClone = this.a.clone(); solveLinearEquationsSet(srcPoint, aClone, yMinusB); } else if (diagonal != null) { for (int i = 0; i < n; i++) { srcPoint[i] = (destPoint[i] - b[i]) / diagonal[i]; } } else { for (int i = 0; i < n; i++) { srcPoint[i] = destPoint[i] - b[i]; } } } /** * Solves the system of linear equations Ax=y by * Gauss elimination algorithm * with partial (column) pivoting. * *

The coordinates of the vector y must be listed in y array. * The elements of the matrix A must be listed, row by row, in the a array: * A={aij}, * aij=a[i*n+j], * i is the index of the row (0..n-1), * j is the index of the column (0..n-1), * n=b.length. * The length a.length of the a array must be equal to the square n2 * of the length n=b.length of the b array. * Empty arrays (n=0) are not allowed. * *

It is possible that there is no required x vector * or there are many different solutions (degenerated cases). * In this case, this method still find some x vector, but some found coordinates * in the x array will probably be * Double.NaN, Double.POSITIVE_INFINITY or Double.NEGATIVE_INFINITY. * *

This method is called in the {@link #inverseMap(double[], double[])} method, * if the matrix A is not {@link #isDiagonal() diagonal}. * *

Warning: this method destroys the content of the passed a and y arrays! * But this method does not occupy any additional memory, unlike {@link #inverseMap(double[], double[])} method. * *

Warning: this method will not work correctly if x and y * is the same Java array. * * @param x the coordinates of x vector, filled by this method. * @param a the elements of A matrix (row by row). * @param y the coordinates of y vector. * @throws NullPointerException if one of the arguments of the method is {@code null}. * @throws IllegalArgumentException if the length of one of the passed arrays is 0, * or if x.length!=y.length, * or if a.length!=x.length2. */ public static void solveLinearEquationsSet(double[] x, double[] a, double[] y) { Objects.requireNonNull(x, "Null x"); Objects.requireNonNull(y, "Null y"); Objects.requireNonNull(a, "Null a"); final int n = x.length; if (y.length != n) throw new IllegalArgumentException("x and y vector lengths mismatch: x.length=" + n + ", y.length=" + y.length); if (a.length != (long) n * (long) n) throw new IllegalArgumentException("Illegal size of A matrix: a.length=" + a.length + " must be equal to x.length^2=" + (long) n * (long) n); // System.out.print("Gauss solving " + n + "x" + n + "... "); // long t1 = System.nanoTime(); // printEquationsSet(null, a, y); // Elimination algorithm for (int k = 0; k < n - 1; k++) { // Let, for example, k=2. Now we have the following equations set (below m=this.n-1): // a00*x0 + a01*x1 + a02*x2 + a03*x3 + ... + a0m*xm = y0 // a11*x1 + a12*x2 + a13*x3 + ... + a1m*xm = y1 // a22*x2 + a23*x3 + ... + a2m*xm = y2 // a32*x2 + a33*x3 + ... + a3m*xm = y3 // a42*x2 + a43*x3 + ... + a4m*xm = y4 // . . . // am2*x2 + am3*x3 + ... + amm*xm = ym // Finding the pivot - the maximal |aik|, i=k,k+1,...,n-1: final int aOfsDiagonal = k * n + k; int pivotIndex = k; double pivot = a[aOfsDiagonal], pivotAbs = StrictMath.abs(pivot); for (int i = k + 1, aOfs = aOfsDiagonal + n; i < n; i++, aOfs += n) { double v = a[aOfs], vAbs = StrictMath.abs(v); if (vAbs > pivotAbs) { pivot = v; pivotAbs = vAbs; pivotIndex = i; } } if (pivotAbs == 0.0) { // Little optimization: avoiding useless futher calculations for (int j = 0; j < n; j++) { x[j] = Double.NaN; } return; } // Exchanging the line #k and line #i: this operation does not change the order of the unknowns if (pivotIndex != k) { double temp = y[k]; y[k] = y[pivotIndex]; y[pivotIndex] = temp; for (int j = k, aOfsK = aOfsDiagonal, aOfs = pivotIndex * n + k; j < n; j++, aOfsK++, aOfs++) { // Don't exchange first k elements in the lines: they are already zero temp = a[aOfsK]; a[aOfsK] = a[aOfs]; a[aOfs] = temp; } } assert a[aOfsDiagonal] == pivot : "Pivot element is not placed to the correct place (k = " + k + ")"; // Now |aik|<=|akk| for all i>k. // Eliminating - subtracting the line #k, multiplied by aik/akk, from all lines #i for (int i = k + 1, aOfs = k * n + n; i < n; i++) { // aOfs refers to the line begin! double q = a[aOfs + k] / pivot; y[i] -= y[k] * q; for (int j = 0; j < k; j++, aOfs++) { assert a[aOfs] == 0.0 : "The line was not filled by zero in previous iterations 0,...," + (k - 1); } a[aOfs++] = 0.0; // not necessary, but the resulting matrix will be "better" (triangular) int aOfsK = aOfsDiagonal + 1; assert aOfs == i * n + k + 1; assert aOfsK == k * n + k + 1; for (int j = k + 1; j < n; j++, aOfsK++, aOfs++) { a[aOfs] -= a[aOfsK] * q; } } } // Now A is triangular: // a00*x0 + a01*x1 + a02*x2 + a03*x3 + ... + a0m*xm = y0 // a11*x1 + a12*x2 + a13*x3 + ... + a1m*xm = y1 // a22*x2 + a23*x3 + ... + a2m*xm = y2 // a33*x3 + ... + a3m*xm = y3 // . . . // amm*xm = ym // Finding the unknowns (x): for (int i = n - 1, aOfs = n * n - 1; i >= 0; i--) { assert aOfs == i * n + n - 1; double v = y[i]; for (int j = n - 1; j > i; j--, aOfs--) { v -= a[aOfs] * x[j]; } x[i] = v / a[aOfs]; aOfs -= i + 1; } // printEquationsSet(x, a, y); // long t2 = System.nanoTime(); // System.out.println("done in " + (t2 - t1) * 1e-3 + " mcs = n^3*" + (t2 - t1) / Math.pow(n, 3) + " ns"); } // Not used in current version. Very slow solution for matrices >3x3. // The private "determinant" method is moved into Matrices class. // static void solveLinearEquationsSetByCramerMethod(double[] x, double[] a, double[] y) { // final int n = y.length; // long t1 = System.nanoTime(); // System.out.print("Cramer solving " + n + "x" + n + "..."); // double det = determinant(n, a); // double[] m = new double[a.length]; // for (int k = 0; k < n; k++) { // for (int i = 0, aOfs = 0, mOfs = 0; i < n; i++) { // for (int j = 0; j < n; j++, aOfs++, mOfs++) { // m[mOfs] = j == k ? y[i] : a[aOfs]; // } // } // x[k] = determinant(n, m) / det; // } // long t2 = System.nanoTime(); // System.out.println(" done in " + (t2 - t1) * 1e-3 + " mcs = n^3*" + (t2 - t1) / Math.pow(n, 3) + " ns"); // } /** * Returns a brief string description of this object. * * @return a brief string description of this object. */ public String toString() { boolean shift = isShift(); StringBuilder sA = new StringBuilder(); if (isDiagonal()) { if (diagonal != null) { sA.append("diag["); for (int k = 0; k < diagonal.length; k++) { if (k > 0) sA.append(","); sA.append(LinearFunc.goodFormat(diagonal[k])); } sA.append("]"); } } else { sA.append("A"); } StringBuilder sB = new StringBuilder(); for (int k = 0; k < n; k++) { if (k > 0) sB.append(","); sB.append(LinearFunc.goodFormat(b[k])); } return "linear (affine) " + n + "-dimensional operator " + sA + "x+b, b=(" + sB + ")" + (shift ? " (shift)" : ""); } static void printEquationsSet(double[] x, double[] a, double[] y) { final int n = y.length; System.out.println(); for (int i = 0; i < n; i++) { double v = 0.0; for (int j = 0; j < n; j++) { if (x != null) { v += a[i * n + j] * x[j]; } System.out.printf("%10.6f*x[%d] " + (j < n - 1 ? "+ " : "= "), a[i * n + j], j); } System.out.printf("%.7f", y[i]); if (x != null) System.out.printf(" (error %.7f)", v - y[i]); System.out.println(); } } /** * The simplest test for this class: finds a linear operator by pairs of points. */ static class Test extends ProjectiveOperator.Test { @Override int numberOfPoints(int dimCount) { return dimCount + 1; } @Override CoordinateTransformationOperator getOperator() { return LinearOperator.getInstanceByPoints(q, p); } void badLinearEquationsSetTest() { double factorial = 1.0; for (int k = 2; k <= 16; k++) { factorial *= k; } double[] a = new double[dimCount * dimCount]; for (int i = 1, aOfs = 0; i <= dimCount; i++) { for (int j = 1; j <= dimCount; j++, aOfs++) { a[aOfs] = factorial / (i + j); } } LinearOperator lo = LinearOperator.getInstance(a, new double[dimCount]); newRndPoints(10, dimCount); for (int k = 0; k < p.length; k++) { double[] srcPoint = p[k].coordinates(); double[] destPoint = new double[srcPoint.length]; q[k] = p[k]; lo.map(destPoint, srcPoint); lo.inverseMap(srcPoint, destPoint); r[k] = Point.valueOf(srcPoint); } double error = maxDiff(); System.out.println("Difference for \"bad\" equation set " + dimCount + "*" + dimCount + " = " + error); LinearOperator lo2 = LinearOperator.getInstance(a, new double[dimCount]); if (!lo.equals(lo2)) throw new AssertionError("Error in equals"); ProjectiveOperator po = ProjectiveOperator.getInstance(a, lo.b(), new double[dimCount], 1.0); if (!lo.equals(po)) throw new AssertionError("Error in equals"); } public static void main(String[] args) { Test test = new Test(); test.init(args); test.badLinearEquationsSetTest(); test.mainTest(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy