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 * Created on June 24, 2010
021 * Author: Mark Chapman
022 */
023
024package org.biojava.nbio.alignment.template;
025
026import org.biojava.nbio.core.alignment.template.ProfilePair;
027import org.biojava.nbio.core.alignment.template.Profile;
028import org.biojava.nbio.core.alignment.template.SubstitutionMatrix;
029import org.biojava.nbio.alignment.template.GapPenalty.Type;
030import org.biojava.nbio.core.sequence.template.Compound;
031import org.biojava.nbio.core.sequence.template.CompoundSet;
032import org.biojava.nbio.core.sequence.template.Sequence;
033import org.slf4j.Logger;
034import org.slf4j.LoggerFactory;
035
036import java.util.ArrayList;
037import java.util.List;
038import java.util.concurrent.ExecutionException;
039import java.util.concurrent.Future;
040
041/**
042 * Implements common code for an {@link Aligner} for a pair of {@link Profile}s.
043 *
044 * @author Mark Chapman
045 * @param <S> each {@link Sequence} in the pair of alignment {@link Profile}s is of type S
046 * @param <C> each element of an {@link AlignedSequence} is a {@link Compound} of type C
047 */
048public abstract class AbstractProfileProfileAligner<S extends Sequence<C>, C extends Compound>
049                extends AbstractMatrixAligner<S, C> implements ProfileProfileAligner<S, C> {
050
051        private final static Logger logger = LoggerFactory.getLogger(AbstractProfileProfileAligner.class);
052
053        // additional input fields
054        private Profile<S, C> query, target;
055
056        // concurrent execution fields
057        private Future<ProfilePair<S, C>> queryFuture, targetFuture;
058
059        // cached fields
060        private List<C> cslist;
061        private float[][] qfrac, tfrac;
062
063        // additional output field
064        protected ProfilePair<S, C> pair;
065
066        /**
067         * Before running a profile-profile alignment, data must be sent in via calls to
068         * {@link #setQuery(Profile)}, {@link #setTarget(Profile)}, {@link #setGapPenalty(GapPenalty)}, and
069         * {@link #setSubstitutionMatrix(SubstitutionMatrix)}.
070         */
071        protected AbstractProfileProfileAligner() {
072        }
073
074        /**
075         * Prepares for a profile-profile alignment.
076         *
077         * @param query the first {@link Profile} of the pair to align
078         * @param target the second {@link Profile} of the pair to align
079         * @param gapPenalty the gap penalties used during alignment
080         * @param subMatrix the set of substitution scores used during alignment
081         */
082        protected AbstractProfileProfileAligner(Profile<S, C> query, Profile<S, C> target, GapPenalty gapPenalty,
083                        SubstitutionMatrix<C> subMatrix) {
084                super(gapPenalty, subMatrix);
085                this.query = query;
086                this.target = target;
087                reset();
088        }
089
090        /**
091         * Prepares for a profile-profile alignment run concurrently.
092         *
093         * @param query the first {@link Profile} of the pair to align, still to be calculated
094         * @param target the second {@link Profile} of the pair to align, still to be calculated
095         * @param gapPenalty the gap penalties used during alignment
096         * @param subMatrix the set of substitution scores used during alignment
097         */
098        protected AbstractProfileProfileAligner(Future<ProfilePair<S, C>> query, Future<ProfilePair<S, C>> target,
099                        GapPenalty gapPenalty, SubstitutionMatrix<C> subMatrix) {
100                super(gapPenalty, subMatrix);
101                queryFuture = query;
102                targetFuture = target;
103                reset();
104        }
105
106        /**
107         * Prepares for a profile-profile alignment run concurrently.
108         *
109         * @param query the first {@link Profile} of the pair to align
110         * @param target the second {@link Profile} of the pair to align, still to be calculated
111         * @param gapPenalty the gap penalties used during alignment
112         * @param subMatrix the set of substitution scores used during alignment
113         */
114        protected AbstractProfileProfileAligner(Profile<S, C> query, Future<ProfilePair<S, C>> target,
115                        GapPenalty gapPenalty, SubstitutionMatrix<C> subMatrix) {
116                super(gapPenalty, subMatrix);
117                this.query = query;
118                targetFuture = target;
119                reset();
120        }
121
122        /**
123         * Prepares for a profile-profile alignment run concurrently.
124         *
125         * @param query the first {@link Profile} of the pair to align, still to be calculated
126         * @param target the second {@link Profile} of the pair to align
127         * @param gapPenalty the gap penalties used during alignment
128         * @param subMatrix the set of substitution scores used during alignment
129         */
130        protected AbstractProfileProfileAligner(Future<ProfilePair<S, C>> query, Profile<S, C> target,
131                        GapPenalty gapPenalty, SubstitutionMatrix<C> subMatrix) {
132                super(gapPenalty, subMatrix);
133                queryFuture = query;
134                this.target = target;
135                reset();
136        }
137
138        /**
139         * Sets the query {@link Profile}.
140         *
141         * @param query the first {@link Profile} of the pair to align
142         */
143        public void setQuery(Profile<S, C> query) {
144                this.query = query;
145                queryFuture = null;
146                reset();
147        }
148
149        /**
150         * Sets the target {@link Profile}.
151         *
152         * @param target the second {@link Profile} of the pair to align
153         */
154        public void setTarget(Profile<S, C> target) {
155                this.target = target;
156                targetFuture = null;
157                reset();
158        }
159
160        // method for ProfileProfileAligner
161
162        @Override
163        public ProfilePair<S, C> getPair() {
164                if (pair == null) {
165                        align();
166                }
167                return pair;
168        }
169
170        // methods for ProfileProfileScorer
171
172        @Override
173        public Profile<S, C> getQuery() {
174                return query;
175        }
176
177        @Override
178        public Profile<S, C> getTarget() {
179                return target;
180        }
181
182        // methods for AbstractMatrixAligner
183
184        @Override
185        protected CompoundSet<C> getCompoundSet() {
186                return (query == null) ? null : query.getCompoundSet();
187        }
188
189        @Override
190        protected List<C> getCompoundsOfQuery() {
191                // TODO replace with consensus sequence
192                return (query == null) ? new ArrayList<C>() : query.getAlignedSequence(1).getAsList();
193        }
194
195        @Override
196        protected List<C> getCompoundsOfTarget() {
197                // TODO replace with consensus sequence
198                return (target == null) ? new ArrayList<C>() : target.getAlignedSequence(1).getAsList();
199        }
200
201        @Override
202        protected int[] getScoreMatrixDimensions() {
203                return new int[] { query.getLength() + 1, target.getLength() + 1, (getGapPenalty().getType() == Type.LINEAR) ?
204                                1 : 3 };
205        }
206
207        @Override
208        protected int getSubstitutionScore(int queryColumn, int targetColumn) {
209                return getSubstitutionScore(qfrac[queryColumn - 1], tfrac[targetColumn - 1]);
210        }
211
212        @Override
213        protected boolean isReady() {
214                // TODO when added to ConcurrencyTools, log completions and exceptions instead of printing stack traces
215                try {
216                        if (query == null && queryFuture != null) {
217                                query = queryFuture.get();
218                        }
219                        if (target == null && targetFuture != null) {
220                                target = targetFuture.get();
221                        }
222                        reset();
223                } catch (InterruptedException e) {
224                        logger.error("Interrupted Exception: ", e);
225                } catch (ExecutionException e) {
226                        logger.error("Execution Exception: ", e);
227                }
228                return query != null && target != null && getGapPenalty() != null && getSubstitutionMatrix() != null &&
229                                query.getCompoundSet().equals(target.getCompoundSet());
230        }
231
232        @Override
233        protected void reset() {
234                super.reset();
235                pair = null;
236                if (query != null && target != null && getGapPenalty() != null && getSubstitutionMatrix() != null &&
237                                query.getCompoundSet().equals(target.getCompoundSet())) {
238                        int maxq = 0, maxt = 0;
239                        cslist = query.getCompoundSet().getAllCompounds();
240                        qfrac = new float[query.getLength()][];
241                        for (int i = 0; i < qfrac.length; i++) {
242                                qfrac[i] = query.getCompoundWeightsAt(i + 1, cslist);
243                                maxq += getSubstitutionScore(qfrac[i], qfrac[i]);
244                        }
245                        tfrac = new float[target.getLength()][];
246                        for (int i = 0; i < tfrac.length; i++) {
247                                tfrac[i] = target.getCompoundWeightsAt(i + 1, cslist);
248                                maxt += getSubstitutionScore(tfrac[i], tfrac[i]);
249                        }
250                        max = Math.max(maxq, maxt);
251                        score = min = isLocal() ? 0 : (int) (2 * getGapPenalty().getOpenPenalty() + (query.getLength() +
252                                        target.getLength()) * getGapPenalty().getExtensionPenalty());
253                }
254        }
255
256        // helper method that scores alignment of two column vectors
257        private int getSubstitutionScore(float[] qv, float[] tv) {
258                float score = 0.0f;
259                for (int q = 0; q < qv.length; q++) {
260                        if (qv[q] > 0.0f) {
261                                for (int t = 0; t < tv.length; t++) {
262                                        if (tv[t] > 0.0f) {
263                                                score += qv[q]*tv[t]*getSubstitutionMatrix().getValue(cslist.get(q), cslist.get(t));
264                                        }
265                                }
266                        }
267                }
268                return Math.round(score);
269        }
270
271}