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

org.biojava.nbio.structure.symmetry.internal.SymmetryAxes Maven / Gradle / Ivy

The newest version!
/*
 *                    BioJava development code
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  If you do not have a copy,
 * see:
 *
 *      http://www.gnu.org/copyleft/lesser.html
 *
 * Copyright for this code is held jointly by the individual
 * authors.  These should be listed in @author doc comments.
 *
 * For more information on the BioJava project and its aims,
 * or to join the biojava-l mailing list, visit the home page
 * at:
 *
 *      http://www.biojava.org/
 *
 */
package org.biojava.nbio.structure.symmetry.internal;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import javax.vecmath.Matrix4d;

import org.biojava.nbio.structure.align.multiple.MultipleAlignment;
import org.biojava.nbio.structure.align.util.RotationAxis;
import org.biojava.nbio.structure.symmetry.internal.CESymmParameters.SymmetryType;

/**
 * Data Structure that stores all the symmetry axis that describe
 * the symmetry of a structure. Generalizes to all types of symmetry,
 * the classic ones (Cn, Dn) and any hierarchical or local symmetries.
 * 

* Hierarchical symmetry can be visualized as a tree, where each level * has a fixed branching factor. Each level of the tree is associated * with a transformation operator, whose order determines the degree of * nodes at that level of the tree. Leaves of the tree implicitly * represent aligned repeats (indexed 0 to n-1), so care must be taken to * keep external references to the repeats (e.g. rows of a * {@link MultipleAlignment} in the same order implied by the tree. *

* Each node of the tree specifies an alignment between those repeats * below each of its children. It is also associated with a symmetry axis, * which is calculated based on the associated operator as well as any parent * operators. * It also stores the parts of the structure (symmetric units) involved * in each axis, in addition to the way to calculate them. *

* This is intended to provide a general axis support for the multiple * repeat alignment optimization and the axis display in Jmol. This * object is related to a MultipleAlignment object that defines the * symmetric units. * * @author Aleix Lafita * @since 4.2.0 * */ public class SymmetryAxes { /* * Implementation note: The tree is a nice explanation and a good image * for developing algorithms, but it is not constructed explicitly. * Instead, we just store one elementary axis for each level and reconstruct * which operators apply to a particular leaf based on that leaf's index. */ /** * Represents an axis of symmetry * @author Spencer Bliven * */ public static class Axis { private Matrix4d operator; private int order; private SymmetryType symmType; private int level; //private int indexInLevel; private int firstRepeat; private RotationAxis rotAxis; public Axis(Matrix4d operator, int order, SymmetryType type, int level, int firstRepeat) { if (order < 2) { throw new IllegalArgumentException("A symmetry axis should divide a structure in > 2 parts"); } if(type != SymmetryType.OPEN && type != SymmetryType.CLOSED) { throw new IllegalArgumentException("Invalid symmetry type. Only OPEN and CLOSED are allowed"); } this.operator = operator; this.order = order; this.symmType = type; setLevel(level); setFirstRepeat(firstRepeat); rotAxis = null; } /** * Get the transformation operator for this axis as an homogeneous matrix * @return the transformation operator */ public Matrix4d getOperator() { return operator; } public void setOperator(Matrix4d op) { this.operator = op; } /** * Get the order of this axis (closed symm) or the number of repeats * (open symm) * @return the order */ public int getOrder() { return order; } /** * @return the symmType (OPEN or CLOSED only) */ public SymmetryType getSymmType() { return symmType; } /** * Get the transformation operator as a rotation axis. For open * symmetry this will have a non-zero screw component. * @return a RotationAxis for this Axis */ public RotationAxis getRotationAxis() { if( rotAxis == null) { rotAxis = new RotationAxis(operator); } return rotAxis; } /** * @return The level of this axis within it's parent hierarchy, or -1 if unset */ public int getLevel() { return level; } /** * * @param level The level of this axis within it's parent hierarchy. Must be positive */ public void setLevel(int level) { if(level < 0) throw new IndexOutOfBoundsException("Level must be positive"); this.level = level; } // /** // * Each level can contain multiple equivalent axes. This index is // * used to distinguish them. // * @return the index of this axis relative to others at the same level // */ // public int getIndexInLevel() { // return indexInLevel; // } // /** // * // * @param indexInLevel the index of this axis relative to others at the same level // */ // public void setIndexInLevel(int indexInLevel) { // if( indexInLevel < 0 || getOrder() <= indexInLevel ) // throw new IndexOutOfBoundsException("Invalid index for order "+getOrder()); // this.indexInLevel = indexInLevel; // } /** * Get the index of the first repeat used by this axis * @return the firstRepeat */ public int getFirstRepeat() { return firstRepeat; } /** * @param firstRepeat the index of the first repeat used by this axis */ public void setFirstRepeat(int firstRepeat) { this.firstRepeat = firstRepeat; } } /** * List of all symmetry axis. They are sorted from higher to lower * in the symmetry hierarchy, where higher means that they apply * more globally and lower means that they apply to a local region * of the higher axis division. */ private final List axes; /** * Constructor. * Initializes variables only. */ public SymmetryAxes(){ axes = new ArrayList<>(); } /** * Adds a new axis of symmetry to the bottom level of the tree * * @param axis the new axis of symmetry found * @param order number of parts that this axis divides the structure in * @param type indicates whether the axis has OPEN or CLOSED symmetry */ public void addAxis(Matrix4d axis, int order, SymmetryType type) { axes.add(new Axis(axis,order,type,axes.size(),0)); } /** * Return a list giving the number of times each axis must be applied * to generate the given repeat. *

* For instance, for a D3 case getAxisCounts(4) would return [2,0], * indicating that repeat 4 is generated by two applications of the 3-fold * axis followed by 0 applications of the two-fold axis. * * @param repeat Index of the desired repeat * @return array of the same length as axes giving the number of times * to apply each axis. */ private int[] getAxisCounts(int repeat) { int[] counts = new int[getNumLevels()]; for(int i = counts.length-1; i >= 0; i--) { int d = axes.get(i).getOrder(); counts[i] = repeat % d; repeat /= d; } assert repeat == 0 : "Invalid repeat index"; return counts; } // /** // * Inverse of {@link #getAxisCounts(int)}; Calculates the repeat for a // * particular number of applications of each axis // * @param counts Number of times to apply each axis // * @return Repeat index // */ // private int getRepeatIndex(int[] counts) { // int repeat = 0; // for(int i = 0; i< counts.length; i++) { // repeat += counts[i]*axes.get(i).getOrder(); // } // return repeat; // } /** * Updates an axis of symmetry, after the superposition changed. * * @param index old axis index * @param newAxis */ public void updateAxis(Integer index, Matrix4d newAxis){ axes.get(index).setOperator(newAxis); } /** * Return the operator for all elementary axes of symmetry of the structure, that is, * the axes stored in the List as unique and from which all the symmetry * axes are constructed. * * @return axes elementary axes of symmetry. */ public List getElementaryAxes(){ List ops = new ArrayList(getNumLevels()); for(Axis axis : axes) { ops.add(axis.getOperator()); } return ops; } /** * Return all elementary axes of symmetry of the structure, that is, * the axes stored in the List as unique and from which all the symmetry * axes are constructed. * * @return axes elementary axes of symmetry. */ public List getElementaryAxesObjects() { return axes; } /** * Get the indices of participating repeats in Cauchy two-line form. *

* Returns two lists of the same length. * The first gives a list of all repeat indices which are aligned * at the specified level of symmetry (e.g. 0 through the degree of this level). * The second list gives the corresponding repeats after applying the * operator once. * * @param level the axis index * @return the double List of repeat relations, or null if the * level is invalid * @see #getRepeatsCyclicForm(int, int) for an equivalent specification with half the memory */ public List> getRepeatRelation(int level){ return getRepeatRelation(level,0); } public List> getRepeatRelation(Axis axis){ return getRepeatRelation(axis.getLevel(),axis.getFirstRepeat()); } public List> getRepeatRelation(int level, int firstRepeat) { Axis axis = axes.get(level); int m = getNumRepeats(level+1);//size of the children int d = axis.getOrder(); // degree of this node int n = m*d; // number of repeats included if(firstRepeat % n != 0) throw new IllegalArgumentException(String.format("Repeat %d cannot start a block at level %s of this tree",firstRepeat,level)); if(axis.getSymmType() == SymmetryType.OPEN) { n -= m; // leave off last child for open symm } List repeats = new ArrayList<>(n); List equiv = new ArrayList<>(n); for(int i=0;i * Each inner list gives a set of equivalent repeats and should have length * equal to the order of the axis' operator. * @param level * @param firstRepeat * @return */ public List> getRepeatsCyclicForm(int level, int firstRepeat) { Axis axis = axes.get(level); int m = getNumRepeats(level+1);//size of the children int d = axis.getOrder(); // degree of this node int n = m*d; // number of repeats included if(firstRepeat % n != 0) { throw new IllegalArgumentException(String.format("Repeat %d cannot start a block at level %s of this tree",firstRepeat,level)); } if(axis.getSymmType() == SymmetryType.OPEN) { n -= m; // leave off last child for open symm } List> repeats = new ArrayList<>(m); for(int i=0;i cycle = new ArrayList<>(d); for(int j=0;j> getRepeatsCyclicForm(Axis axis) { return getRepeatsCyclicForm(axis.getLevel(),axis.getFirstRepeat()); } public List> getRepeatsCyclicForm(int level) { return getRepeatsCyclicForm(level,0); } public String getRepeatsCyclicForm(Axis axis, List repeats) { if(repeats.size() != getNumRepeats()) { throw new IllegalArgumentException("Mismatch in the number of repeats"); } return getRepeatsCyclicForm(getRepeatsCyclicForm(axis), repeats); } public static String getRepeatsCyclicForm(List> cycleForm, List repeats) { StringBuilder str = new StringBuilder(); for(List cycle : cycleForm) { str.append("("); Iterator cycleIt = cycle.iterator(); str.append(repeats.get(cycleIt.next())); //should be at least one while(cycleIt.hasNext()) { str.append(";") .append(repeats.get( cycleIt.next() )); } str.append(")"); } return str.toString(); } /** * Return the transformation that needs to be applied to a * repeat in order to superimpose onto repeat 0. * * @param repeat the repeat index * @return transformation matrix for the repeat */ public Matrix4d getRepeatTransform(int repeat){ Matrix4d transform = new Matrix4d(); transform.setIdentity(); int[] counts = getAxisCounts(repeat); for(int t = counts.length-1; t>=0; t--) { if( counts[t] == 0 ) continue; Matrix4d axis = new Matrix4d(axes.get(t).getOperator()); for(int i=0;i=0; t--) { if(counts[t] == 0) continue; if (counts[t] > 0) { Matrix4d axis = new Matrix4d(axes.get(t).getOperator()); for(int i=0;i getSymmetryAxes(){ List symmAxes = new ArrayList<>(); Matrix4d prior = new Matrix4d(); prior.setIdentity(); getSymmetryAxes(symmAxes,prior,0,0); return symmAxes; } /** * Recursive helper * @param symmAxes output list * @param prior transformation aligning the first repeat of this axis with the first overall * @param level current level */ private void getSymmetryAxes(List symmAxes, Matrix4d prior, int level, int firstRepeat) { if(level >= getNumLevels() ) { return; } Axis elem = axes.get(level); Matrix4d elemOp = elem.getOperator(); // Current axis: // elementary maps B -> A // prior maps I -> A and J -> B // want J -> I = J -> B -> A <- I= inv(prior) * elementary * prior Matrix4d currAxisOp = new Matrix4d(prior); currAxisOp.invert(); currAxisOp.mul(elemOp); currAxisOp.mul(prior); Axis currAxis = new Axis(currAxisOp,elem.getOrder(),elem.getSymmType(),level,firstRepeat); symmAxes.add(currAxis); //Remember that all degrees are at least 2 getSymmetryAxes(symmAxes,prior,level+1,firstRepeat); //New prior is elementary^d*prior Matrix4d newPrior = new Matrix4d(elemOp); newPrior.mul(prior); int childSize = getNumRepeats(level+1); getSymmetryAxes(symmAxes,newPrior,level+1,firstRepeat+childSize); for(int d=2;d= degrees.get(level-1) ) // throw new IndexOutOfBoundsException("Axis number out of bounds"); // // Convert axisNum into a count of // // } /** * Get the number of repeats. This is equal to the product of all degrees. * @return Number of repeats (leaves of the tree). */ public int getNumRepeats() { return getNumRepeats(0); } /** * Get the number of leaves from a node at the specified level. This is * equal to the product of all degrees at or below the level. * @param level level of the tree to cut at * @return Number of repeats (leaves of the tree). */ private int getNumRepeats(int level) { int size = 1; // Return 1 for illegally high level if(level < getNumLevels()) { for(Axis axis : axes.subList(level, getNumLevels())) { size *= axis.getOrder(); } } return size; } /** * Get the first repeat index of each axis of a specified level. * @param level level of the tree to cut at * @return List of first Repeats of each index, sorted in ascending order */ public List getFirstRepeats(int level) { List firstRepeats = new ArrayList<>(); int m = getNumRepeats(level+1); //size of the level int d = axes.get(level).getOrder(); //degree of this level int n = m*d; // number of repeats included in each axis for (int firstRepeat = 0; firstRepeat < getNumRepeats(); firstRepeat+=n) firstRepeats.add(firstRepeat); return firstRepeats; } public Axis getElementaryAxis(int level) { return axes.get(level); } public int getNumLevels() { return axes.size(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy