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.quaternary;
022
023import java.util.ArrayList;
024import java.util.Collections;
025import java.util.List;
026import java.util.Map;
027import java.util.stream.Collectors;
028
029import javax.vecmath.Matrix4d;
030
031import org.biojava.nbio.structure.Atom;
032import org.biojava.nbio.structure.align.multiple.MultipleAlignment;
033import org.biojava.nbio.structure.align.multiple.util.MultipleAlignmentScorer;
034import org.biojava.nbio.structure.cluster.Subunit;
035import org.biojava.nbio.structure.cluster.SubunitCluster;
036
037/**
038 * Result of a Quaternary Structure Alignment {@link QsAlign}. The QsAlignResult
039 * holds the original inputs of the algorithm and the results and scores of the
040 * alignment.
041 * 
042 * @author Aleix Lafita
043 * @since 5.0.0
044 *
045 */
046public class QsAlignResult {
047
048        private List<SubunitCluster> clusters;
049
050        private List<Subunit> subunits1;
051        private List<Subunit> subunits2;
052
053        private Map<Integer, Integer> subunitMap;
054        private MultipleAlignment alignment;
055
056        private QsRelation relation;
057
058        /**
059         * The Constructor of the result takes the same inputs as the
060         * {@link QsAlign} algorithm.
061         * 
062         * @param subunits1
063         * @param subunits2
064         */
065        public QsAlignResult(List<Subunit> subunits1, List<Subunit> subunits2) {
066
067                this.subunits1 = subunits1;
068                this.subunits2 = subunits2;
069
070                subunitMap = Collections.emptyMap();
071                relation = QsRelation.DIFFERENT;
072
073        }
074
075        /**
076         * Original Subunits of the first group.
077         * 
078         * @return an unmodifiable view of the original List
079         */
080        public List<Subunit> getSubunits1() {
081                return Collections.unmodifiableList(subunits1);
082        }
083
084        /**
085         * Original Subunits of the second group.
086         * 
087         * @return an unmodifiable view of the original List
088         */
089        public List<Subunit> getSubunits2() {
090                return Collections.unmodifiableList(subunits2);
091        }
092
093        /**
094         * Map of Subunit equivalencies from the first to the second group.
095         * 
096         * @return an unmodifiable view of the original Map
097         */
098        public Map<Integer, Integer> getSubunitMap() {
099
100                if (subunitMap == null)
101                        return Collections.emptyMap();
102
103                return Collections.unmodifiableMap(subunitMap);
104        }
105
106        /**
107         * Map of Subunit equivalencies from the first to the second group.
108         * 
109         * @param subunitMap
110         */
111        public void setSubunitMap(Map<Integer, Integer> subunitMap) {
112
113                // Check consistency of the map
114                if (Collections.max(subunitMap.keySet()) > subunits1.size()
115                                | Collections.max(subunitMap.values()) > subunits2.size())
116                        throw new IndexOutOfBoundsException(
117                                        "Subunit Map index higher than Subunit List size.");
118
119                // Update the relation enum
120                if (subunitMap.size() == 0) {
121                        relation = QsRelation.DIFFERENT;
122                } else if (subunitMap.keySet().size() == subunits1.size()) {
123                        if (subunitMap.values().size() == subunits2.size()) {
124                                relation = QsRelation.EQUIVALENT;
125                        } else {
126                                relation = QsRelation.PARTIAL_COMPLETE;
127                        }
128                } else {
129                        if (subunitMap.values().size() == subunits2.size()) {
130                                relation = QsRelation.PARTIAL_COMPLETE;
131                        } else {
132                                relation = QsRelation.PARTIAL_INCOMPLETE;
133                        }
134                }
135
136                this.subunitMap = subunitMap;
137        }
138
139        /**
140         * The length of the alignment is the number of Subunit equivalencies it
141         * contains. This is equivalent to the size of the Subunit Map.
142         * 
143         * @return length of the alignment
144         */
145        public int length() {
146                if (subunitMap == null)
147                        return 0;
148
149                return subunitMap.size();
150        }
151
152        /**
153         * The transformation 4D matrix that needs to be applied to the second group
154         * of Subunits to superimpose them onto the first group of Subunits, given
155         * the equivalent residues in the SubunitCluster and the Subunit
156         * equivalencies.
157         * <p>
158         * This is equivalent to
159         * multipleAlignment.getBlockSet(0).getTransformations().get(1).
160         * 
161         * @return Matrix4d
162         */
163        public Matrix4d getTransform() {
164
165                if (alignment == null)
166                        return null;
167
168                return alignment.getBlockSet(0).getTransformations().get(1);
169        }
170
171        /**
172         * The RMSD between the equivalent residues of the equivalent Subunits after
173         * superposition of the Subunit groups. This is equivalent to
174         * multipleAlignment.getScore(MultipleAlignmentScorer.RMSD).
175         * 
176         * @return rmsd
177         */
178        public double getRmsd() {
179
180                if (alignment == null)
181                        return -1.0;
182                if (alignment.getScore(MultipleAlignmentScorer.RMSD) == null)
183                        return MultipleAlignmentScorer.getRMSD(alignment);
184
185                return alignment.getScore(MultipleAlignmentScorer.RMSD);
186        }
187
188        /**
189         * The quaternary structure relation {@link QsRelation} between the two
190         * groups of Subunits.
191         * 
192         * @return relation
193         */
194        public QsRelation getRelation() {
195                return relation;
196        }
197
198        /**
199         * The quaternary structure relation {@link QsRelation} between the two
200         * groups of Subunits.
201         * 
202         * @param relation
203         */
204        public void setRelation(QsRelation relation) {
205                this.relation = relation;
206        }
207
208        /**
209         * The alignment that specifies the residue equivalencies of the equivalent
210         * Subunits.
211         * 
212         * @return alignment as a MultipleAlignment object
213         */
214        public MultipleAlignment getAlignment() {
215                return alignment;
216        }
217
218        /**
219         * The alignment that specifies the residue equivalencies of the equivalent
220         * Subunits.
221         * 
222         * @param alignment
223         *            a MultipleAlignment object
224         */
225        public void setAlignment(MultipleAlignment alignment) {
226                this.alignment = alignment;
227        }
228
229        /**
230         * Return the aligned subunits of the first Subunit group, in the alignment
231         * order.
232         * 
233         * @return a List of Subunits in the alignment order
234         */
235        public List<Subunit> getAlignedSubunits1() {
236
237                List<Subunit> aligned = new ArrayList<Subunit>(subunitMap.size());
238
239                for (Integer key : subunitMap.keySet())
240                        aligned.add(subunits1.get(key));
241
242                return aligned;
243        }
244
245        /**
246         * Return the aligned subunits of the second Subunit group, in the alignment
247         * order.
248         * 
249         * @return a List of Subunits in the alignment order
250         */
251        public List<Subunit> getAlignedSubunits2() {
252
253                List<Subunit> aligned = new ArrayList<Subunit>(subunitMap.size());
254
255                for (Integer key : subunitMap.keySet())
256                        aligned.add(subunits2.get(subunitMap.get(key)));
257
258                return aligned;
259        }
260
261        public void setClusters(List<SubunitCluster> clusters) {
262                this.clusters = clusters;
263        }
264
265        public Atom[] getAlignedAtomsForSubunits1(int index) {
266
267                // Obtain the indices of the clustered subunits
268                for (SubunitCluster cluster : clusters) {
269                        if (cluster.getSubunits().contains(subunits1.get(index))) {
270                                return cluster.getAlignedAtomsSubunit(cluster.getSubunits()
271                                                .indexOf(subunits1.get(index)));
272                        }
273                }
274                return null;
275        }
276
277        public Atom[] getAlignedAtomsForSubunits2(int index) {
278
279                // Obtain the indices of the clustered subunits
280                for (SubunitCluster cluster : clusters) {
281                        if (cluster.getSubunits().contains(subunits2.get(index))) {
282                                return cluster.getAlignedAtomsSubunit(cluster.getSubunits()
283                                                .indexOf(subunits2.get(index)));
284                        }
285                }
286                return null;
287        }
288
289        @Override
290        public String toString() {
291                return "QsAlignResult [relation="
292                                + relation
293                                + ", rmsd="
294                                + getRmsd()
295                                + ", length="
296                                + length()
297                                + ", Aligned 1: "
298                                + getAlignedSubunits1().stream().map(s -> s.getName())
299                                                .collect(Collectors.toList())
300                                + ", Aligned 2: "
301                                + getAlignedSubunits2().stream().map(s -> s.getName())
302                                                .collect(Collectors.toList()) + "]";
303        }
304
305}