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.AlignTools;
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.getName2() != null) {
165                        structureIdentifiers = Arrays.<StructureIdentifier> asList(
166                                        new StructureName(afp.getName1()),
167                                        new StructureName(afp.getName2()));
168                }
169                algorithmName = afp.getAlgorithmName();
170                version = afp.getVersion();
171                calculationTime = afp.getCalculationTime();
172
173                MultipleAlignment msa = new MultipleAlignmentImpl(this);
174                setMultipleAlignments(Arrays.asList(msa));
175
176                // Convert the rotation and translation to a Matrix4D and set it
177                Matrix4d ident = new Matrix4d();
178                ident.setIdentity();
179                Matrix[] rot = afp.getBlockRotationMatrix();
180                Atom[] shift = afp.getBlockShiftVector();
181
182                // Create a BlockSet for every block in AFPChain if flexible
183                if (flexible) {
184                        for (int bs = 0; bs < afp.getBlockNum(); bs++) {
185                                BlockSet blockSet = new BlockSetImpl(msa);
186                                Matrix4d blockTr = null;
187                                try {
188                                        blockTr = Calc.getTransformation(rot[bs], shift[bs]);
189                                } catch (IndexOutOfBoundsException e) {
190                                        blockTr = ident;
191                                } catch (NullPointerException e) {
192                                        blockTr = ident;
193                                }
194                                blockSet.setTransformations(Arrays.asList(ident, blockTr));
195                                Block block = new BlockImpl(blockSet);
196                                block.setAlignRes(new ArrayList<List<Integer>>());
197                                block.getAlignRes().add(new ArrayList<Integer>());
198                                block.getAlignRes().add(new ArrayList<Integer>());
199
200                                // Set the transformation of the BlockSet
201                                Matrix rotB = afp.getBlockRotationMatrix()[bs];
202                                Atom shiftB = afp.getBlockShiftVector()[bs];
203                                Matrix4d transformB = Calc.getTransformation(rotB, shiftB);
204                                blockSet.setTransformations(Arrays.asList(ident, transformB));
205
206                                // Convert the optimal alignment to a Block
207                                for (int i = 0; i < afp.getOptAln()[bs][0].length; i++) {
208                                        block.getAlignRes().get(0).add(afp.getOptAln()[bs][0][i]);
209                                        block.getAlignRes().get(1).add(afp.getOptAln()[bs][1][i]);
210                                }
211                        }
212                } // Create a Block for every block in AFPChain if not flexible
213                else {
214                        BlockSet blockSet = new BlockSetImpl(msa);
215                        Matrix4d blockTr = null;
216                        try {
217                                blockTr = Calc.getTransformation(rot[0], shift[0]);
218                        } catch (IndexOutOfBoundsException e) {
219                                blockTr = ident;
220                        } catch (NullPointerException e) {
221                                blockTr = ident;
222                        }
223                        blockSet.setTransformations(Arrays.asList(ident, blockTr));
224                        for (int bs = 0; bs < afp.getBlockNum(); bs++) {
225                                Block block = new BlockImpl(blockSet);
226                                block.setAlignRes(new ArrayList<List<Integer>>());
227                                block.getAlignRes().add(new ArrayList<Integer>());
228                                block.getAlignRes().add(new ArrayList<Integer>());
229
230                                // Convert the optimal alignment to a Block
231                                for (int i = 0; i < afp.getOptAln()[bs][0].length; i++) {
232                                        block.getAlignRes().get(0).add(afp.getOptAln()[bs][0][i]);
233                                        block.getAlignRes().get(1).add(afp.getOptAln()[bs][1][i]);
234                                }
235                        }
236                }
237
238                // Copy the scores stored in the AFPChain
239                msa.putScore(MultipleAlignmentScorer.PROBABILITY, afp.getProbability());
240                msa.putScore(MultipleAlignmentScorer.AVGTM_SCORE, afp.getTMScore());
241                msa.putScore(MultipleAlignmentScorer.CE_SCORE, afp.getAlignScore());
242                msa.putScore(MultipleAlignmentScorer.RMSD, afp.getTotalRmsdOpt());
243        }
244
245        @Override
246        public MultipleAlignmentEnsembleImpl clone() {
247                return new MultipleAlignmentEnsembleImpl(this);
248        }
249
250        @Override
251        public String getAlgorithmName() {
252                return algorithmName;
253        }
254
255        @Override
256        public void setAlgorithmName(String algorithmName) {
257                this.algorithmName = algorithmName;
258        }
259
260        @Override
261        public String getVersion() {
262                return version;
263        }
264
265        @Override
266        public void setVersion(String version) {
267                this.version = version;
268        }
269
270        @Override
271        public Long getIoTime() {
272                return ioTime;
273        }
274
275        @Override
276        public void setIoTime(Long millis) {
277                this.ioTime = millis;
278        }
279
280        @Override
281        public Long getCalculationTime() {
282                return calculationTime;
283        }
284
285        @Override
286        public void setCalculationTime(Long millis) {
287                this.calculationTime = millis;
288        }
289
290        @Override
291        public List<StructureIdentifier> getStructureIdentifiers() {
292                return structureIdentifiers;
293        }
294
295        @Override
296        public void setStructureIdentifiers(List<StructureIdentifier> structureNames) {
297                this.structureIdentifiers = structureNames;
298        }
299
300        @Override
301        public List<Atom[]> getAtomArrays() {
302                if (atomArrays == null) {
303                        try {
304                                updateAtomArrays();
305                        } catch (IOException e) {
306                                throw new NullPointerException(e.getMessage());
307                        } catch (StructureException e) {
308                                throw new NullPointerException(e.getMessage());
309                        }
310                }
311                return atomArrays;
312        }
313
314        @Override
315        public void setAtomArrays(List<Atom[]> atomArrays) {
316                this.atomArrays = atomArrays;
317        }
318
319        /**
320         * Force the atom arrays to regenerate based on
321         * {@link #getStructureIdentifiers()}.
322         *
323         * @throws IOException
324         * @throws StructureException
325         */
326        public void updateAtomArrays() throws IOException, StructureException {
327                AtomCache cache = new AtomCache();
328                atomArrays = new ArrayList<Atom[]>();
329                for (StructureIdentifier name : getStructureIdentifiers()) {
330                        Atom[] array = cache.getRepresentativeAtoms(name);
331                        atomArrays.add(array);
332                }
333        }
334
335        @Override
336        public List<Matrix> getDistanceMatrix() {
337                if (distanceMatrix == null)
338                        updateDistanceMatrix();
339                return distanceMatrix;
340        }
341
342        /**
343         * Force recalculation of the distance matrices.
344         */
345        public void updateDistanceMatrix() {
346
347                // Reset the distance Matrix variable
348                distanceMatrix = new ArrayList<Matrix>();
349
350                for (int s = 0; s < size(); s++) {
351                        Atom[] ca = atomArrays.get(s);
352                        Matrix distMat = AlignTools.getDistanceMatrix(ca, ca);
353                        distanceMatrix.add(distMat);
354                }
355        }
356
357        @Override
358        public List<MultipleAlignment> getMultipleAlignments() {
359
360                if (multipleAlignments == null) {
361                        multipleAlignments = new ArrayList<MultipleAlignment>();
362                }
363                return multipleAlignments;
364        }
365
366        @Override
367        public MultipleAlignment getMultipleAlignment(int index) {
368                return multipleAlignments.get(index);
369        }
370
371        @Override
372        public void setMultipleAlignments(List<MultipleAlignment> alignments) {
373                this.multipleAlignments = alignments;
374        }
375
376        @Override
377        public void addMultipleAlignment(MultipleAlignment alignment) {
378                if (multipleAlignments == null) {
379                        multipleAlignments = new ArrayList<MultipleAlignment>();
380                }
381                multipleAlignments.add(alignment);
382                alignment.setEnsemble(this);
383        }
384
385        @Override
386        public int size() {
387                if (structureIdentifiers != null)
388                        return structureIdentifiers.size();
389                else if (atomArrays != null)
390                        return atomArrays.size();
391                else {
392                        throw new IndexOutOfBoundsException(
393                                        "Empty ensemble: names == null && atoms == null");
394                }
395        }
396
397        @Override
398        public void clear() {
399                super.clear();
400                distanceMatrix = null;
401                for (MultipleAlignment a : getMultipleAlignments())
402                        a.clear();
403        }
404}