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         */
071        public MultipleAlignmentEnsembleImpl() {
072
073                algorithmName = null;
074                version = null;
075                ioTime = null;
076                calculationTime = null;
077
078                structureIdentifiers = null;
079                atomArrays = null;
080                distanceMatrix = null;
081                multipleAlignments = null;
082        }
083
084        /**
085         * Constructor using structure identifiers.
086         *
087         * @param structureIdentifiers
088         *            List of Structure names, that can be parsed by
089         *            {@link AtomCache}.
090         */
091        public MultipleAlignmentEnsembleImpl(
092                        List<StructureIdentifier> structureIdentifiers) {
093                this();
094                setStructureIdentifiers(structureIdentifiers);
095        }
096
097        /**
098         * Copy constructor. This copies recursively all member variables, including
099         * MultipleAlignments, Atom arrays and cached variables.
100         *
101         * @param e
102         *            MultipleAlignmentEnsemble to copy.
103         */
104        public MultipleAlignmentEnsembleImpl(MultipleAlignmentEnsembleImpl e) {
105
106                super(e); // Copy the scores
107                algorithmName = e.algorithmName;
108                version = e.version;
109                ioTime = e.ioTime;
110                calculationTime = e.calculationTime;
111
112                distanceMatrix = null;
113                if (e.distanceMatrix != null) {
114                        // Make a deep copy of everything
115                        distanceMatrix = new ArrayList<>();
116                        for (Matrix mat : e.distanceMatrix) {
117                                distanceMatrix.add((Matrix) mat.clone());
118                        }
119                }
120
121                multipleAlignments = null;
122                if (e.multipleAlignments != null) {
123                        // Make a deep copy of everything
124                        multipleAlignments = new ArrayList<>();
125                        for (MultipleAlignment msa : e.multipleAlignments) {
126                                MultipleAlignment newMSA = msa.clone();
127                                newMSA.setEnsemble(this);
128                                multipleAlignments.add(newMSA);
129                        }
130                }
131
132                if (e.atomArrays != null) {
133                        atomArrays = new ArrayList<>(e.atomArrays);
134                }
135                if (e.structureIdentifiers != null) {
136                        structureIdentifiers = new ArrayList<>(
137                                        e.structureIdentifiers);
138                }
139        }
140
141        /**
142         * Constructor from an AFPChain instance. Creates an equivalent pairwise
143         * alignment, but in the MultipleAlignment format.
144         *
145         * @param afp
146         *            pairwise alignment
147         * @param ca1
148         *            Atoms of the first strcuture
149         * @param ca2
150         *            Atoms of the second structure
151         * @param flexible
152         *            true if the alignment is flexible (use BlockSets)
153         */
154        public MultipleAlignmentEnsembleImpl(AFPChain afp, Atom[] ca1, Atom[] ca2,
155                        boolean flexible) {
156
157                this();
158                // Copy all the creation and algorithm information
159                atomArrays = Arrays.asList(ca1, ca2);
160                if (afp.getName1() != null && !afp.getName1().isEmpty() &&
161                                afp.getName2() != null && !afp.getName2().isEmpty()) {
162                        structureIdentifiers = Arrays.<StructureIdentifier> asList(
163                                        new StructureName(afp.getName1()),
164                                        new StructureName(afp.getName2()));
165                }
166                algorithmName = afp.getAlgorithmName();
167                version = afp.getVersion();
168                calculationTime = afp.getCalculationTime();
169
170                MultipleAlignment msa = new MultipleAlignmentImpl(this);
171                setMultipleAlignments(Arrays.asList(msa));
172
173                // Convert the rotation and translation to a Matrix4D and set it
174                Matrix4d ident = new Matrix4d();
175                ident.setIdentity();
176                Matrix[] rot = afp.getBlockRotationMatrix();
177                Atom[] shift = afp.getBlockShiftVector();
178
179                // Create a BlockSet for every block in AFPChain if flexible
180                if (flexible) {
181                        for (int bs = 0; bs < afp.getBlockNum(); bs++) {
182                                BlockSet blockSet = new BlockSetImpl(msa);
183                                Matrix4d blockTr = null;
184                                try {
185                                        blockTr = Calc.getTransformation(rot[bs], shift[bs]);
186                                } catch (IndexOutOfBoundsException e) {
187                                        blockTr = ident;
188                                } catch (NullPointerException e) {
189                                        blockTr = ident;
190                                }
191                                blockSet.setTransformations(Arrays.asList(ident, blockTr));
192                                Block block = new BlockImpl(blockSet);
193                                block.setAlignRes(new ArrayList<List<Integer>>());
194                                block.getAlignRes().add(new ArrayList<Integer>());
195                                block.getAlignRes().add(new ArrayList<Integer>());
196
197                                // Set the transformation of the BlockSet
198                                Matrix rotB = afp.getBlockRotationMatrix()[bs];
199                                Atom shiftB = afp.getBlockShiftVector()[bs];
200                                Matrix4d transformB = Calc.getTransformation(rotB, shiftB);
201                                blockSet.setTransformations(Arrays.asList(ident, transformB));
202
203                                // Convert the optimal alignment to a Block
204                                for (int i = 0; i < afp.getOptAln()[bs][0].length; i++) {
205                                        block.getAlignRes().get(0).add(afp.getOptAln()[bs][0][i]);
206                                        block.getAlignRes().get(1).add(afp.getOptAln()[bs][1][i]);
207                                }
208                        }
209                } // Create a Block for every block in AFPChain if not flexible
210                else {
211                        BlockSet blockSet = new BlockSetImpl(msa);
212                        Matrix4d blockTr = null;
213                        try {
214                                blockTr = Calc.getTransformation(rot[0], shift[0]);
215                        } catch (IndexOutOfBoundsException e) {
216                                blockTr = ident;
217                        } catch (NullPointerException e) {
218                                blockTr = ident;
219                        }
220                        blockSet.setTransformations(Arrays.asList(ident, blockTr));
221                        for (int bs = 0; bs < afp.getBlockNum(); bs++) {
222                                Block block = new BlockImpl(blockSet);
223                                block.setAlignRes(new ArrayList<List<Integer>>());
224                                block.getAlignRes().add(new ArrayList<Integer>());
225                                block.getAlignRes().add(new ArrayList<Integer>());
226
227                                // Convert the optimal alignment to a Block
228                                for (int i = 0; i < afp.getOptAln()[bs][0].length; i++) {
229                                        block.getAlignRes().get(0).add(afp.getOptAln()[bs][0][i]);
230                                        block.getAlignRes().get(1).add(afp.getOptAln()[bs][1][i]);
231                                }
232                        }
233                }
234
235                // Copy the scores stored in the AFPChain
236                msa.putScore(MultipleAlignmentScorer.PROBABILITY, afp.getProbability());
237                msa.putScore(MultipleAlignmentScorer.AVGTM_SCORE, afp.getTMScore());
238                msa.putScore(MultipleAlignmentScorer.CE_SCORE, afp.getAlignScore());
239                msa.putScore(MultipleAlignmentScorer.RMSD, afp.getTotalRmsdOpt());
240        }
241
242        @Override
243        public MultipleAlignmentEnsembleImpl clone() {
244                return new MultipleAlignmentEnsembleImpl(this);
245        }
246
247        @Override
248        public String getAlgorithmName() {
249                return algorithmName;
250        }
251
252        @Override
253        public void setAlgorithmName(String algorithmName) {
254                this.algorithmName = algorithmName;
255        }
256
257        @Override
258        public String getVersion() {
259                return version;
260        }
261
262        @Override
263        public void setVersion(String version) {
264                this.version = version;
265        }
266
267        @Override
268        public Long getIoTime() {
269                return ioTime;
270        }
271
272        @Override
273        public void setIoTime(Long millis) {
274                this.ioTime = millis;
275        }
276
277        @Override
278        public Long getCalculationTime() {
279                return calculationTime;
280        }
281
282        @Override
283        public void setCalculationTime(Long millis) {
284                this.calculationTime = millis;
285        }
286
287        @Override
288        public List<StructureIdentifier> getStructureIdentifiers() {
289                return structureIdentifiers;
290        }
291
292        @Override
293        public void setStructureIdentifiers(List<StructureIdentifier> structureNames) {
294                this.structureIdentifiers = structureNames;
295        }
296
297        @Override
298        public List<Atom[]> getAtomArrays() {
299                if (atomArrays == null) {
300                        try {
301                                updateAtomArrays();
302                        } catch (IOException e) {
303                                throw new NullPointerException(e.getMessage());
304                        } catch (StructureException e) {
305                                throw new NullPointerException(e.getMessage());
306                        }
307                }
308                return atomArrays;
309        }
310
311        @Override
312        public void setAtomArrays(List<Atom[]> atomArrays) {
313                this.atomArrays = atomArrays;
314        }
315
316        /**
317         * Force the atom arrays to regenerate based on
318         * {@link #getStructureIdentifiers()}.
319         *
320         * @throws IOException
321         * @throws StructureException
322         */
323        public void updateAtomArrays() throws IOException, StructureException {
324                AtomCache cache = new AtomCache();
325                atomArrays = new ArrayList<>();
326                for (StructureIdentifier name : getStructureIdentifiers()) {
327                        Atom[] array = cache.getRepresentativeAtoms(name);
328                        atomArrays.add(array);
329                }
330        }
331
332        @Override
333        public List<Matrix> getDistanceMatrix() {
334                if (distanceMatrix == null)
335                        updateDistanceMatrix();
336                return distanceMatrix;
337        }
338
339        /**
340         * Force recalculation of the distance matrices.
341         */
342        public void updateDistanceMatrix() {
343
344                // Reset the distance Matrix variable
345                distanceMatrix = new ArrayList<>();
346
347                for (int s = 0; s < size(); s++) {
348                        Atom[] ca = atomArrays.get(s);
349                        Matrix distMat = AlignUtils.getDistanceMatrix(ca, ca);
350                        distanceMatrix.add(distMat);
351                }
352        }
353
354        @Override
355        public List<MultipleAlignment> getMultipleAlignments() {
356
357                if (multipleAlignments == null) {
358                        multipleAlignments = new ArrayList<>();
359                }
360                return multipleAlignments;
361        }
362
363        @Override
364        public MultipleAlignment getMultipleAlignment(int index) {
365                return multipleAlignments.get(index);
366        }
367
368        @Override
369        public void setMultipleAlignments(List<MultipleAlignment> alignments) {
370                this.multipleAlignments = alignments;
371        }
372
373        @Override
374        public void addMultipleAlignment(MultipleAlignment alignment) {
375                if (multipleAlignments == null) {
376                        multipleAlignments = new ArrayList<>();
377                }
378                multipleAlignments.add(alignment);
379                alignment.setEnsemble(this);
380        }
381
382        @Override
383        public int size() {
384                if (structureIdentifiers != null)
385                        return structureIdentifiers.size();
386                else if (atomArrays != null)
387                        return atomArrays.size();
388                else {
389                        throw new IndexOutOfBoundsException(
390                                        "Empty ensemble: names == null && atoms == null");
391                }
392        }
393
394        @Override
395        public void clear() {
396                super.clear();
397                distanceMatrix = null;
398                for (MultipleAlignment a : getMultipleAlignments())
399                        a.clear();
400        }
401}