001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021package org.biojava.nbio.structure.align.multiple;
022
023import java.io.IOException;
024import java.io.Serializable;
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.List;
028
029import javax.vecmath.Matrix4d;
030
031import org.biojava.nbio.structure.Atom;
032import org.biojava.nbio.structure.Calc;
033import org.biojava.nbio.structure.StructureException;
034import org.biojava.nbio.structure.StructureIdentifier;
035import org.biojava.nbio.structure.align.client.StructureName;
036import org.biojava.nbio.structure.align.helper.AlignUtils;
037import org.biojava.nbio.structure.align.model.AFPChain;
038import org.biojava.nbio.structure.align.multiple.util.MultipleAlignmentScorer;
039import org.biojava.nbio.structure.align.util.AtomCache;
040import org.biojava.nbio.structure.jama.Matrix;
041
042/**
043 * A general implementation of a {@link MultipleAlignmentEnsemble}.
044 *
045 * @author Aleix Lafita
046 * @since 4.1.0
047 *
048 */
049public class MultipleAlignmentEnsembleImpl extends AbstractScoresCache
050                implements MultipleAlignmentEnsemble, Serializable, Cloneable {
051
052        private static final long serialVersionUID = -5732485866623431898L;
053
054        // Creation Properties
055        private String algorithmName;
056        private String version;
057        private Long ioTime;
058        private Long calculationTime;
059
060        // Structure Identifiers
061        private List<StructureIdentifier> structureIdentifiers;
062        private List<Atom[]> atomArrays;
063        private List<Matrix> distanceMatrix;
064
065        private List<MultipleAlignment> multipleAlignments;
066
067        /**
068         * Default Constructor. Empty ensemble, no structures assigned.
069         *
070         * @return MultipleAlignmentEnsemble an empty ensemble instance.
071         */
072        public MultipleAlignmentEnsembleImpl() {
073
074                algorithmName = null;
075                version = null;
076                ioTime = null;
077                calculationTime = null;
078
079                structureIdentifiers = null;
080                atomArrays = null;
081                distanceMatrix = null;
082                multipleAlignments = null;
083        }
084
085        /**
086         * Constructor using structure identifiers.
087         *
088         * @param structureIdentifiers
089         *            List of Structure names, that can be parsed by
090         *            {@link AtomCache}.
091         * @return MultipleAlignmentEnsemble an ensemble with the structures.
092         */
093        public MultipleAlignmentEnsembleImpl(
094                        List<StructureIdentifier> structureIdentifiers) {
095                this();
096                setStructureIdentifiers(structureIdentifiers);
097        }
098
099        /**
100         * Copy constructor. This copies recursively all member variables, including
101         * MultipleAlignments, Atom arrays and cached variables.
102         *
103         * @param e
104         *            MultipleAlignmentEnsemble to copy.
105         * @return MultipleAlignmentEnsemble identical copy of the input ensemble.
106         */
107        public MultipleAlignmentEnsembleImpl(MultipleAlignmentEnsembleImpl e) {
108
109                super(e); // Copy the scores
110                algorithmName = e.algorithmName;
111                version = e.version;
112                ioTime = e.ioTime;
113                calculationTime = e.calculationTime;
114
115                distanceMatrix = null;
116                if (e.distanceMatrix != null) {
117                        // Make a deep copy of everything
118                        distanceMatrix = new ArrayList<Matrix>();
119                        for (Matrix mat : e.distanceMatrix) {
120                                distanceMatrix.add((Matrix) mat.clone());
121                        }
122                }
123
124                multipleAlignments = null;
125                if (e.multipleAlignments != null) {
126                        // Make a deep copy of everything
127                        multipleAlignments = new ArrayList<MultipleAlignment>();
128                        for (MultipleAlignment msa : e.multipleAlignments) {
129                                MultipleAlignment newMSA = msa.clone();
130                                newMSA.setEnsemble(this);
131                                multipleAlignments.add(newMSA);
132                        }
133                }
134
135                if (e.atomArrays != null) {
136                        atomArrays = new ArrayList<Atom[]>(e.atomArrays);
137                }
138                if (e.structureIdentifiers != null) {
139                        structureIdentifiers = new ArrayList<StructureIdentifier>(
140                                        e.structureIdentifiers);
141                }
142        }
143
144        /**
145         * Constructor from an AFPChain instance. Creates an equivalent pairwise
146         * alignment, but in the MultipleAlignment format.
147         *
148         * @param afp
149         *            pairwise alignment
150         * @param ca1
151         *            Atoms of the first strcuture
152         * @param ca2
153         *            Atoms of the second structure
154         * @param flexible
155         *            true if the alignment is flexible (use BlockSets)
156         * @return MultipleAlignmentEnsemble an ensemble
157         */
158        public MultipleAlignmentEnsembleImpl(AFPChain afp, Atom[] ca1, Atom[] ca2,
159                        boolean flexible) {
160
161                this();
162                // Copy all the creation and algorithm information
163                atomArrays = Arrays.asList(ca1, ca2);
164                if (afp.getName1() != null && !afp.getName1().isEmpty() &&
165                                afp.getName2() != null && !afp.getName2().isEmpty()) {
166                        structureIdentifiers = Arrays.<StructureIdentifier> asList(
167                                        new StructureName(afp.getName1()),
168                                        new StructureName(afp.getName2()));
169                }
170                algorithmName = afp.getAlgorithmName();
171                version = afp.getVersion();
172                calculationTime = afp.getCalculationTime();
173
174                MultipleAlignment msa = new MultipleAlignmentImpl(this);
175                setMultipleAlignments(Arrays.asList(msa));
176
177                // Convert the rotation and translation to a Matrix4D and set it
178                Matrix4d ident = new Matrix4d();
179                ident.setIdentity();
180                Matrix[] rot = afp.getBlockRotationMatrix();
181                Atom[] shift = afp.getBlockShiftVector();
182
183                // Create a BlockSet for every block in AFPChain if flexible
184                if (flexible) {
185                        for (int bs = 0; bs < afp.getBlockNum(); bs++) {
186                                BlockSet blockSet = new BlockSetImpl(msa);
187                                Matrix4d blockTr = null;
188                                try {
189                                        blockTr = Calc.getTransformation(rot[bs], shift[bs]);
190                                } catch (IndexOutOfBoundsException e) {
191                                        blockTr = ident;
192                                } catch (NullPointerException e) {
193                                        blockTr = ident;
194                                }
195                                blockSet.setTransformations(Arrays.asList(ident, blockTr));
196                                Block block = new BlockImpl(blockSet);
197                                block.setAlignRes(new ArrayList<List<Integer>>());
198                                block.getAlignRes().add(new ArrayList<Integer>());
199                                block.getAlignRes().add(new ArrayList<Integer>());
200
201                                // Set the transformation of the BlockSet
202                                Matrix rotB = afp.getBlockRotationMatrix()[bs];
203                                Atom shiftB = afp.getBlockShiftVector()[bs];
204                                Matrix4d transformB = Calc.getTransformation(rotB, shiftB);
205                                blockSet.setTransformations(Arrays.asList(ident, transformB));
206
207                                // Convert the optimal alignment to a Block
208                                for (int i = 0; i < afp.getOptAln()[bs][0].length; i++) {
209                                        block.getAlignRes().get(0).add(afp.getOptAln()[bs][0][i]);
210                                        block.getAlignRes().get(1).add(afp.getOptAln()[bs][1][i]);
211                                }
212                        }
213                } // Create a Block for every block in AFPChain if not flexible
214                else {
215                        BlockSet blockSet = new BlockSetImpl(msa);
216                        Matrix4d blockTr = null;
217                        try {
218                                blockTr = Calc.getTransformation(rot[0], shift[0]);
219                        } catch (IndexOutOfBoundsException e) {
220                                blockTr = ident;
221                        } catch (NullPointerException e) {
222                                blockTr = ident;
223                        }
224                        blockSet.setTransformations(Arrays.asList(ident, blockTr));
225                        for (int bs = 0; bs < afp.getBlockNum(); bs++) {
226                                Block block = new BlockImpl(blockSet);
227                                block.setAlignRes(new ArrayList<List<Integer>>());
228                                block.getAlignRes().add(new ArrayList<Integer>());
229                                block.getAlignRes().add(new ArrayList<Integer>());
230
231                                // Convert the optimal alignment to a Block
232                                for (int i = 0; i < afp.getOptAln()[bs][0].length; i++) {
233                                        block.getAlignRes().get(0).add(afp.getOptAln()[bs][0][i]);
234                                        block.getAlignRes().get(1).add(afp.getOptAln()[bs][1][i]);
235                                }
236                        }
237                }
238
239                // Copy the scores stored in the AFPChain
240                msa.putScore(MultipleAlignmentScorer.PROBABILITY, afp.getProbability());
241                msa.putScore(MultipleAlignmentScorer.AVGTM_SCORE, afp.getTMScore());
242                msa.putScore(MultipleAlignmentScorer.CE_SCORE, afp.getAlignScore());
243                msa.putScore(MultipleAlignmentScorer.RMSD, afp.getTotalRmsdOpt());
244        }
245
246        @Override
247        public MultipleAlignmentEnsembleImpl clone() {
248                return new MultipleAlignmentEnsembleImpl(this);
249        }
250
251        @Override
252        public String getAlgorithmName() {
253                return algorithmName;
254        }
255
256        @Override
257        public void setAlgorithmName(String algorithmName) {
258                this.algorithmName = algorithmName;
259        }
260
261        @Override
262        public String getVersion() {
263                return version;
264        }
265
266        @Override
267        public void setVersion(String version) {
268                this.version = version;
269        }
270
271        @Override
272        public Long getIoTime() {
273                return ioTime;
274        }
275
276        @Override
277        public void setIoTime(Long millis) {
278                this.ioTime = millis;
279        }
280
281        @Override
282        public Long getCalculationTime() {
283                return calculationTime;
284        }
285
286        @Override
287        public void setCalculationTime(Long millis) {
288                this.calculationTime = millis;
289        }
290
291        @Override
292        public List<StructureIdentifier> getStructureIdentifiers() {
293                return structureIdentifiers;
294        }
295
296        @Override
297        public void setStructureIdentifiers(List<StructureIdentifier> structureNames) {
298                this.structureIdentifiers = structureNames;
299        }
300
301        @Override
302        public List<Atom[]> getAtomArrays() {
303                if (atomArrays == null) {
304                        try {
305                                updateAtomArrays();
306                        } catch (IOException e) {
307                                throw new NullPointerException(e.getMessage());
308                        } catch (StructureException e) {
309                                throw new NullPointerException(e.getMessage());
310                        }
311                }
312                return atomArrays;
313        }
314
315        @Override
316        public void setAtomArrays(List<Atom[]> atomArrays) {
317                this.atomArrays = atomArrays;
318        }
319
320        /**
321         * Force the atom arrays to regenerate based on
322         * {@link #getStructureIdentifiers()}.
323         *
324         * @throws IOException
325         * @throws StructureException
326         */
327        public void updateAtomArrays() throws IOException, StructureException {
328                AtomCache cache = new AtomCache();
329                atomArrays = new ArrayList<Atom[]>();
330                for (StructureIdentifier name : getStructureIdentifiers()) {
331                        Atom[] array = cache.getRepresentativeAtoms(name);
332                        atomArrays.add(array);
333                }
334        }
335
336        @Override
337        public List<Matrix> getDistanceMatrix() {
338                if (distanceMatrix == null)
339                        updateDistanceMatrix();
340                return distanceMatrix;
341        }
342
343        /**
344         * Force recalculation of the distance matrices.
345         */
346        public void updateDistanceMatrix() {
347
348                // Reset the distance Matrix variable
349                distanceMatrix = new ArrayList<Matrix>();
350
351                for (int s = 0; s < size(); s++) {
352                        Atom[] ca = atomArrays.get(s);
353                        Matrix distMat = AlignUtils.getDistanceMatrix(ca, ca);
354                        distanceMatrix.add(distMat);
355                }
356        }
357
358        @Override
359        public List<MultipleAlignment> getMultipleAlignments() {
360
361                if (multipleAlignments == null) {
362                        multipleAlignments = new ArrayList<MultipleAlignment>();
363                }
364                return multipleAlignments;
365        }
366
367        @Override
368        public MultipleAlignment getMultipleAlignment(int index) {
369                return multipleAlignments.get(index);
370        }
371
372        @Override
373        public void setMultipleAlignments(List<MultipleAlignment> alignments) {
374                this.multipleAlignments = alignments;
375        }
376
377        @Override
378        public void addMultipleAlignment(MultipleAlignment alignment) {
379                if (multipleAlignments == null) {
380                        multipleAlignments = new ArrayList<MultipleAlignment>();
381                }
382                multipleAlignments.add(alignment);
383                alignment.setEnsemble(this);
384        }
385
386        @Override
387        public int size() {
388                if (structureIdentifiers != null)
389                        return structureIdentifiers.size();
390                else if (atomArrays != null)
391                        return atomArrays.size();
392                else {
393                        throw new IndexOutOfBoundsException(
394                                        "Empty ensemble: names == null && atoms == null");
395                }
396        }
397
398        @Override
399        public void clear() {
400                super.clear();
401                distanceMatrix = null;
402                for (MultipleAlignment a : getMultipleAlignments())
403                        a.clear();
404        }
405}