Java tutorial
/* Copyright 2009-2015 David Hadka * * This file is part of the MOEA Framework. * * The MOEA Framework is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * The MOEA Framework is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the MOEA Framework. If not, see <http://www.gnu.org/licenses/>. */ package iDynoOptimizer.MOEAFramework26.src.org.moeaframework.util; import java.util.ArrayList; import java.util.List; import org.apache.commons.math3.linear.MatrixUtils; import org.apache.commons.math3.linear.RealMatrix; import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.core.PRNG; /** * Builds rotation matrices of any dimension constructively from one or more * 2D planar rotations. Three types of construction mechanisms are provided: * * <ol> * <li>Specific rotation planes and thetas * <pre> new RotationMatrixBuilder(3).rotatePlane(0,1).withTheta(Math.PI/4) * .rotatePlane(1,2).withTheta(Math.PI/8).create()</pre> * <li>Specific rotation planes with random thetas * <pre> new RotationMatrixBuilder(3).rotatePlane(0,1).rotatePlane(1,2) * .withRandomThetas().create()</pre> * <li>Random subplanes with random thetas * <pre> new RotationMatrixBuilder(3).rotateK(2).create()</pre> * </ol> * * <p> * References: * <ol> * <li>Aguilera, A. and Pérez-Aguila, R. "General n-Dimensional * Rotations." WSCG 2004, pp. 1-8, 2004. * </ol> */ public class RotationMatrixBuilder { /** * The 2D rotation planes. */ private static class Plane { /** * The first axis defining the rotation plane. */ private final int firstAxis; /** * The second axis defining the rotation plane. */ private final int secondAxis; /** * The rotation angle in radians; or {@code NaN} if no rotation is * assigned. */ private double theta; /** * Constructs a 2D rotation plane around the specified axes. * * @param firstAxis the first axis * @param secondAxis the second axis */ public Plane(int firstAxis, int secondAxis) { super(); this.firstAxis = firstAxis; this.secondAxis = secondAxis; theta = Double.NaN; } /** * Returns the first axis defining the rotation plane. * * @return the first axis defining the rotation plane */ public int getFirstAxis() { return firstAxis; } /** * Returns the second axis defining the rotation plane. * * @return the second axis defining the rotation plane */ public int getSecondAxis() { return secondAxis; } /** * Returns the rotation angle in radians; or {@code NaN} if no rotation * is assigned. * * @return the rotation angle in radians; or {@code NaN} if no rotation * is assigned */ public double getTheta() { return theta; } /** * Sets the rotation angle in radians. * * @param theta the rotation angle in radians */ public void setTheta(double theta) { this.theta = theta; } } /** * The dimension of rotation matrices produced by this builder. */ private final int dimension; /** * The 2D rotation planes currently used by this builder. */ private final List<Plane> planes; /** * Constructs a rotation matrix builder for the given dimension. * * @param dimension the dimension of rotation matrices produced by * this builder */ public RotationMatrixBuilder(int dimension) { super(); this.dimension = dimension; planes = new ArrayList<Plane>(); } /** * Adds a rotation around the 2D rotation plane defined by the two axes. * The plane is initially unrotated, but can be assigned a specific rotation * angle if followed by {@link #withTheta(double)}. * * @param i the first axis * @param j the second axis * @return a reference to this rotation matrix builder */ public RotationMatrixBuilder rotatePlane(int i, int j) { if ((i < 0) || (i >= dimension) || (j < 0) || (j >= dimension) || (i == j)) { throw new IllegalArgumentException("invalid plane"); } planes.add(new Plane(i, j)); return this; } /** * Sets the rotation angle, in radians, of the last 2D rotation plane * added to this builder. This method should always only follow * invocations of {@link #rotatePlane(int, int)}. * * @param theta the rotation angle in radians * @return a reference to this rotation matrix builder */ public RotationMatrixBuilder withTheta(double theta) { planes.get(planes.size() - 1).setTheta(theta); return this; } /** * Sets the rotation angle, in radians, of all 2D rotation planes added to * this builder. * * @param theta the rotation angle in radians * @return a reference to this rotation matrix builder */ public RotationMatrixBuilder withThetas(double theta) { for (Plane plane : planes) { plane.setTheta(theta); } return this; } /** * Assigns random rotation angles to all 2D rotation planes added to this * builder. * * @return a reference to this rotation matrix builder */ public RotationMatrixBuilder withRandomThetas() { for (Plane plane : planes) { plane.setTheta(PRNG.nextDouble(0.0, 2.0 * Math.PI)); } return this; } /** * Rotates all 2D rotation planes and assigns each a random rotation angle. * * @return a reference to this rotation matrix builder */ public RotationMatrixBuilder rotateAll() { for (int i = 0; i < dimension - 1; i++) { for (int j = i + 1; j < dimension; j++) { Plane plane = new Plane(i, j); plane.setTheta(PRNG.nextDouble(0.0, 2.0 * Math.PI)); planes.add(plane); } } return this; } /** * Rotates {@code k} randomly-selected but unique 2D rotation planes and * assigns each a random rotation angle. For an {@code N}-dimension * rotation matrix, there exist {@code (N choose 2)} 2D rotation planes. * * @param k the number of randomly-selected 2D rotation planes * @return a reference to this rotation matrix builder */ public RotationMatrixBuilder rotateK(int k) { List<Plane> tempPlanes = new ArrayList<Plane>(); //generate the list of all available rotation planes for (int i = 0; i < dimension - 1; i++) { for (int j = i + 1; j < dimension; j++) { tempPlanes.add(new Plane(i, j)); } } if ((k < 0) || (k > tempPlanes.size())) { throw new IllegalArgumentException("invalid number of planes"); } //shuffle to randomize which planes are rotated PRNG.shuffle(tempPlanes); //clear any existing planes planes.clear(); //assign the first k planes for (int i = 0; i < k; i++) { Plane plane = tempPlanes.get(i); plane.setTheta(PRNG.nextDouble(0.0, 2.0 * Math.PI)); planes.add(plane); } return this; } /** * Returns the rotation matrix resulting from applying all 2D rotation * planes and angles added to this builder. * * @return the rotation matrix resulting from applying all 2D rotation * planes and angles added to this builder */ public RealMatrix create() { RealMatrix rotation = newIdentityMatrix(); for (Plane plane : planes) { double theta = plane.getTheta(); if (Double.isNaN(theta)) { continue; } rotation = rotation.multiply(newRotationMatrix(plane.getFirstAxis(), plane.getSecondAxis(), theta)); } return rotation; } /** * Returns an identity matrix. * * @return an identity matrix */ private RealMatrix newIdentityMatrix() { RealMatrix identity = MatrixUtils.createRealMatrix(dimension, dimension); for (int i = 0; i < dimension; i++) { identity.setEntry(i, i, 1.0); } return identity; } /** * Returns a rotation matrix for rotating about the 2D plane defined by * the specified axes. * * @param i the first axis * @param j the second axis * @param theta the rotation angle in radians * @return a rotation matrix for rotating about the 2D plane defined by * the specified axes */ private RealMatrix newRotationMatrix(int i, int j, double theta) { RealMatrix rotation = newIdentityMatrix(); rotation.setEntry(i, i, Math.cos(theta)); rotation.setEntry(i, j, -Math.sin(theta)); rotation.setEntry(j, i, Math.sin(theta)); rotation.setEntry(j, j, Math.cos(theta)); return rotation; } }