/** * Reallocates an array with a new size, and copies the contents of the old array to the new * array. * * @param oldArray the old array, to be reallocated. * @param newSize the new array size. * @return A new array with the same contents. */ public static Object resizeArray(Object oldArray, int newSize) { int oldSize = java.lang.reflect.Array.getLength(oldArray); @SuppressWarnings("rawtypes") Class elementType = oldArray.getClass().getComponentType(); Object newArray = java.lang.reflect.Array.newInstance(elementType, newSize); int preserveLength = Math.min(oldSize, newSize); if (preserveLength > 0) System.arraycopy(oldArray, 0, newArray, 0, preserveLength); return newArray; }
/** * Tries to detect symmetry in an alignment. * * <p>Conceptually, an alignment is a function f:A->B between two sets of integers. The function * may have simple topology (meaning that if two elements of A are close, then their images in B * will also be close), or may have more complex topology (such as a circular permutation). This * function checks <i>alignment</i> against a reference function <i>identity</i>, which should * have simple topology. It then tries to determine the symmetry order of <i>alignment</i> * relative to <i>identity</i>, up to a maximum order of <i>maxSymmetry</i>. * * <p><strong>Details</strong><br> * Considers the offset (in number of residues) which a residue moves after undergoing <i>n</i> * alternating transforms by alignment and identity. If <i>n</i> corresponds to the intrinsic * order of the alignment, this will be small. This algorithm tries increasing values of <i>n</i> * and looks for abrupt decreases in the root mean squared offset. If none are found at * <i>n</i><=maxSymmetry, the alignment is reported as non-symmetric. * * @param alignment The alignment to test for symmetry * @param identity An alignment with simple topology which approximates the sequential * relationship between the two proteins. Should map in the reverse direction from alignment. * @param maxSymmetry Maximum symmetry to consider. High values increase the calculation time and * can lead to overfitting. * @param minimumMetricChange Percent decrease in root mean squared offsets in order to declare * symmetry. 0.4f seems to work well for CeSymm. * @return The order of symmetry of alignment, or 1 if no order <= maxSymmetry is found. * @see IdentityMap For a simple identity function */ public static int getSymmetryOrder( Map<Integer, Integer> alignment, Map<Integer, Integer> identity, final int maxSymmetry, final float minimumMetricChange) { List<Integer> preimage = new ArrayList<Integer>(alignment.keySet()); // currently unmodified List<Integer> image = new ArrayList<Integer>(preimage); int bestSymmetry = 1; double bestMetric = Double.POSITIVE_INFINITY; // lower is better boolean foundSymmetry = false; if (debug) { logger.trace("Symm\tPos\tDelta"); } for (int n = 1; n <= maxSymmetry; n++) { int deltasSq = 0; int numDeltas = 0; // apply alignment for (int i = 0; i < image.size(); i++) { Integer pre = image.get(i); Integer intermediate = (pre == null ? null : alignment.get(pre)); Integer post = (intermediate == null ? null : identity.get(intermediate)); image.set(i, post); if (post != null) { int delta = post - preimage.get(i); deltasSq += delta * delta; numDeltas++; if (debug) { logger.debug("%d\t%d\t%d\n", n, preimage.get(i), delta); } } } // Metrics: RMS compensates for the trend of smaller numDeltas with higher order // Not normalizing by numDeltas favors smaller orders double metric = Math.sqrt((double) deltasSq / numDeltas); // root mean squared distance if (!foundSymmetry && metric < bestMetric * minimumMetricChange) { // n = 1 is never the best symmetry if (bestMetric < Double.POSITIVE_INFINITY) { foundSymmetry = true; } bestSymmetry = n; bestMetric = metric; } // When debugging need to loop over everything. Unneeded in production if (!debug && foundSymmetry) { break; } } if (foundSymmetry) { return bestSymmetry; } else { return 1; } }
/** * Takes a structure and sequence corresponding to an alignment between a structure or sequence * and itself (or even a structure with a sequence), where the result has a circular permutation * site {@link cpSite} residues to the right. * * @param first The unpermuted sequence * @param second The sequence permuted by cpSite * @param cpSite The number of residues from the beginning of the sequence at which the circular * permutation site occurs; can be positive or negative; values greater than the length of the * sequence are acceptable * @throws StructureException */ public static AFPChain cpFastaToAfpChain( ProteinSequence first, ProteinSequence second, Structure structure, int cpSite) throws StructureException { if (structure == null) { throw new IllegalArgumentException("The structure is null"); } if (first == null) { throw new IllegalArgumentException("The sequence is null"); } // we need to find the ungapped CP site int gappedCpShift = 0; int ungappedCpShift = 0; while (ungappedCpShift < Math.abs(cpSite)) { char c; try { if (cpSite <= 0) { c = second.getSequenceAsString().charAt(gappedCpShift); } else { c = second.getSequenceAsString().charAt(first.getLength() - 1 - gappedCpShift); } } catch (StringIndexOutOfBoundsException e) { throw new IllegalArgumentException("CP site of " + cpSite + " is wrong"); } if (c != '-') { ungappedCpShift++; } gappedCpShift++; } Atom[] ca1 = StructureTools.getRepresentativeAtomArray(structure); Atom[] ca2 = StructureTools.getRepresentativeAtomArray( structure); // can't use cloneCAArray because it doesn't set parent // group.chain.structure ProteinSequence antipermuted = null; try { antipermuted = new ProteinSequence( SequenceTools.permuteCyclic(second.getSequenceAsString(), gappedCpShift)); } catch (CompoundNotFoundException e) { // this can't happen, the original sequence comes from a ProteinSequence logger.error( "Unexpected error while creating protein sequence: {}. This is most likely a bug.", e.getMessage()); } ResidueNumber[] residues = StructureSequenceMatcher.matchSequenceToStructure(first, structure); ResidueNumber[] antipermutedResidues = StructureSequenceMatcher.matchSequenceToStructure(antipermuted, structure); ResidueNumber[] nonpermutedResidues = new ResidueNumber[antipermutedResidues.length]; SequenceTools.permuteCyclic(antipermutedResidues, nonpermutedResidues, -gappedCpShift); // nullify ResidueNumbers that have a lowercase sequence character if (first.getUserCollection() != null) { CasePreservingProteinSequenceCreator.setLowercaseToNull(first, residues); } if (second.getUserCollection() != null) { CasePreservingProteinSequenceCreator.setLowercaseToNull(second, nonpermutedResidues); } // for (int i = 0; i < residues.length; i++) { // if (residues[i] == null) { // System.out.print("="); // } else { // System.out.print(sequence.getSequenceAsString().charAt(i)); // } // } // System.out.println(); // for (int i = 0; i < residues.length; i++) { // if (nonpermutedResidues[i] == null) { // System.out.print("="); // } else { // System.out.print(second.getSequenceAsString().charAt(i)); // } // } // System.out.println(); return buildAlignment(ca1, ca2, residues, nonpermutedResidues); }