From 7f5ab7d1f58d870622968e0e6a430f33403b8e4f Mon Sep 17 00:00:00 2001 From: gmungoc Date: Thu, 23 Feb 2017 15:48:57 +0000 Subject: [PATCH] JAL-2403 JAL-1483 changes to ScoreModelI hierarchy and signatures to allow distance and similarity models to be used for Tree and PCA --- src/jalview/analysis/NJTree.java | 166 +++++++++++++------- src/jalview/analysis/PCA.java | 93 +++++------ .../analysis/scoremodels/FeatureDistanceModel.java | 31 ++-- .../analysis/scoremodels/PIDDistanceModel.java | 12 +- .../scoremodels/PairwiseDistanceModel.java | 128 --------------- .../analysis/scoremodels/SWDistanceModel.java | 24 +-- src/jalview/analysis/scoremodels/ScoreMatrix.java | 28 +++- src/jalview/analysis/scoremodels/ScoreModels.java | 24 +-- src/jalview/api/analysis/DistanceModelI.java | 52 ------ src/jalview/api/analysis/DistanceScoreModelI.java | 24 +++ .../analysis}/PairwiseScoreModelI.java | 2 +- src/jalview/api/analysis/ScoreModelI.java | 33 ++++ .../api/analysis/SimilarityScoreModelI.java | 24 +++ src/jalview/appletgui/TreeCanvas.java | 18 +-- src/jalview/appletgui/TreePanel.java | 11 +- src/jalview/datamodel/SequenceNode.java | 6 +- src/jalview/gui/AlignFrame.java | 4 +- src/jalview/gui/PCAPanel.java | 89 +++++++---- src/jalview/gui/TreeCanvas.java | 49 +++--- src/jalview/gui/TreeChooser.java | 6 +- src/jalview/gui/TreePanel.java | 49 +++--- src/jalview/jbgui/GPCAPanel.java | 12 +- src/jalview/schemes/Blosum62ColourScheme.java | 2 +- src/jalview/viewmodel/PCAModel.java | 50 +++--- .../scoremodels/FeatureDistanceModelTest.java | 56 ++++--- .../analysis/scoremodels/ScoreMatrixTest.java | 2 +- .../analysis/scoremodels/ScoreModelsTest.java | 60 +++---- test/jalview/util/ComparisonTest.java | 2 + 28 files changed, 533 insertions(+), 524 deletions(-) delete mode 100644 src/jalview/analysis/scoremodels/PairwiseDistanceModel.java delete mode 100644 src/jalview/api/analysis/DistanceModelI.java create mode 100644 src/jalview/api/analysis/DistanceScoreModelI.java rename src/jalview/{analysis/scoremodels => api/analysis}/PairwiseScoreModelI.java (95%) create mode 100644 src/jalview/api/analysis/ScoreModelI.java create mode 100644 src/jalview/api/analysis/SimilarityScoreModelI.java diff --git a/src/jalview/analysis/NJTree.java b/src/jalview/analysis/NJTree.java index 7140867..5f268ae 100644 --- a/src/jalview/analysis/NJTree.java +++ b/src/jalview/analysis/NJTree.java @@ -21,7 +21,9 @@ package jalview.analysis; import jalview.analysis.scoremodels.ScoreModels; -import jalview.api.analysis.DistanceModelI; +import jalview.api.analysis.DistanceScoreModelI; +import jalview.api.analysis.ScoreModelI; +import jalview.api.analysis.SimilarityScoreModelI; import jalview.datamodel.AlignmentView; import jalview.datamodel.BinaryNode; import jalview.datamodel.CigarArray; @@ -31,6 +33,7 @@ import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; import jalview.datamodel.SequenceNode; import jalview.io.NewickFile; +import jalview.math.MatrixI; import java.util.Enumeration; import java.util.List; @@ -67,15 +70,15 @@ public class NJTree int noClus; - float[][] distance; + MatrixI distance; int mini; int minj; - float ri; + double ri; - float rj; + double rj; Vector groups = new Vector(); @@ -83,9 +86,9 @@ public class NJTree SequenceNode top; - float maxDistValue; + double maxDistValue; - float maxheight; + double maxheight; int ycount; @@ -214,25 +217,25 @@ public class NJTree * * @param sequence * DOCUMENT ME! - * @param type + * @param treeType * DOCUMENT ME! - * @param pwtype + * @param modelType * DOCUMENT ME! * @param start * DOCUMENT ME! * @param end * DOCUMENT ME! */ - public NJTree(SequenceI[] sequence, AlignmentView seqData, String type, - String pwtype, DistanceModelI sm, int start, int end) + public NJTree(SequenceI[] sqs, AlignmentView seqView, String treeType, + String modelType, ScoreModelI sm, int start, int end) { - this.sequence = sequence; + this.sequence = sqs; this.node = new Vector(); - this.type = type; - this.pwtype = pwtype; - if (seqData != null) + this.type = treeType; + this.pwtype = modelType; + if (seqView != null) { - this.seqData = seqData; + this.seqData = seqView; } else { @@ -246,16 +249,16 @@ public class NJTree this.seqData = new AlignmentView(sdata, start); } // System.err.println("Made seqData");// dbg - if (!(type.equals(NEIGHBOUR_JOINING))) + if (!(treeType.equals(NEIGHBOUR_JOINING))) { - type = AVERAGE_DISTANCE; + treeType = AVERAGE_DISTANCE; } - if (sm == null && !(pwtype.equals("PID"))) + if (sm == null && !(modelType.equals("PID"))) { - if (ScoreModels.getInstance().forName(pwtype) == null) + if (ScoreModels.getInstance().forName(modelType) == null) { - pwtype = "BLOSUM62"; + modelType = "BLOSUM62"; } } @@ -271,7 +274,22 @@ public class NJTree noseqs = i++; - distance = findDistances(sm); + if (sm instanceof DistanceScoreModelI) + { + distance = ((DistanceScoreModelI) sm).findDistances(seqData); + } + else if (sm instanceof SimilarityScoreModelI) + { + /* + * compute similarity and invert it to give a distance measure + */ + MatrixI result = ((SimilarityScoreModelI) sm) + .findSimilarities(seqData); + double maxScore = result.getMaxValue(); + result.subtractAllFrom(maxScore); + distance = result; + } + // System.err.println("Made distances");// dbg makeLeaves(); // System.err.println("Made leaves");// dbg @@ -453,7 +471,7 @@ public class NJTree */ public Cluster joinClusters(int i, int j) { - float dist = distance[i][j]; + double dist = distance.getValue(i, j); int noi = cluster.elementAt(i).value.length; int noj = cluster.elementAt(j).value.length; @@ -520,7 +538,7 @@ public class NJTree * DOCUMENT ME! */ public void findNewNJDistances(SequenceNode tmpi, SequenceNode tmpj, - float dist) + double dist) { tmpi.dist = ((dist + ri) - rj) / 2; @@ -548,10 +566,10 @@ public class NJTree * DOCUMENT ME! */ public void findNewDistances(SequenceNode tmpi, SequenceNode tmpj, - float dist) + double dist) { - float ih = 0; - float jh = 0; + double ih = 0; + double jh = 0; SequenceNode sni = tmpi; SequenceNode snj = tmpj; @@ -586,13 +604,16 @@ public class NJTree int noj = cluster.elementAt(j).value.length; // New distances from cluster to others - float[] newdist = new float[noseqs]; + double[] newdist = new double[noseqs]; for (int l = 0; l < noseqs; l++) { if ((l != i) && (l != j)) { - newdist[l] = ((distance[i][l] * noi) + (distance[j][l] * noj)) + // newdist[l] = ((distance[i][l] * noi) + (distance[j][l] * noj)) + // / (noi + noj); + newdist[l] = ((distance.getValue(i, l) * noi) + (distance.getValue( + j, l) * noj)) / (noi + noj); } else @@ -603,8 +624,10 @@ public class NJTree for (int ii = 0; ii < noseqs; ii++) { - distance[i][ii] = newdist[ii]; - distance[ii][i] = newdist[ii]; + // distance[i][ii] = newdist[ii]; + // distance[ii][i] = newdist[ii]; + distance.setValue(i, ii, newdist[ii]); + distance.setValue(ii, i, newdist[ii]); } } @@ -620,13 +643,16 @@ public class NJTree { // New distances from cluster to others - float[] newdist = new float[noseqs]; + double[] newdist = new double[noseqs]; for (int l = 0; l < noseqs; l++) { if ((l != i) && (l != j)) { - newdist[l] = ((distance[i][l] + distance[j][l]) - distance[i][j]) / 2; + // newdist[l] = ((distance[i][l] + distance[j][l]) - distance[i][j]) / + // 2; + newdist[l] = (distance.getValue(i, l) + distance.getValue(j, l) - distance + .getValue(i, j)) / 2; } else { @@ -636,8 +662,10 @@ public class NJTree for (int ii = 0; ii < noseqs; ii++) { - distance[i][ii] = newdist[ii]; - distance[ii][i] = newdist[ii]; + // distance[i][ii] = newdist[ii]; + // distance[ii][i] = newdist[ii]; + distance.setValue(i, ii, newdist[ii]); + distance.setValue(ii, i, newdist[ii]); } } @@ -651,15 +679,16 @@ public class NJTree * * @return DOCUMENT ME! */ - public float findr(int i, int j) + public double findr(int i, int j) { - float tmp = 1; + double tmp = 1; for (int k = 0; k < noseqs; k++) { if ((k != i) && (k != j) && (done[k] != 1)) { - tmp = tmp + distance[i][k]; + // tmp = tmp + distance[i][k]; + tmp = tmp + distance.getValue(i, k); } } @@ -676,9 +705,9 @@ public class NJTree * * @return DOCUMENT ME! */ - public float findMinNJDistance() + public double findMinNJDistance() { - float min = 100000; + double min = Double.MAX_VALUE; for (int i = 0; i < (noseqs - 1); i++) { @@ -686,7 +715,9 @@ public class NJTree { if ((done[i] != 1) && (done[j] != 1)) { - float tmp = distance[i][j] - (findr(i, j) + findr(j, i)); + // float tmp = distance[i][j] - (findr(i, j) + findr(j, i)); + double tmp = distance.getValue(i, j) + - (findr(i, j) + findr(j, i)); if (tmp < min) { @@ -707,9 +738,9 @@ public class NJTree * * @return DOCUMENT ME! */ - public float findMinDistance() + public double findMinDistance() { - float min = 100000; + double min = Double.MAX_VALUE; for (int i = 0; i < (noseqs - 1); i++) { @@ -717,12 +748,14 @@ public class NJTree { if ((done[i] != 1) && (done[j] != 1)) { - if (distance[i][j] < min) + // if (distance[i][j] < min) + if (distance.getValue(i, j) < min) { mini = i; minj = j; - min = distance[i][j]; + // min = distance[i][j]; + min = distance.getValue(i, j); } } } @@ -734,24 +767,42 @@ public class NJTree /** * Calculate a distance matrix given the sequence input data and score model * - * @return similarity matrix used to compute tree + * @return */ - public float[][] findDistances(DistanceModelI _pwmatrix) + public MatrixI findDistances(ScoreModelI scoreModel) { + MatrixI result = null; - float[][] dist = new float[noseqs][noseqs]; - if (_pwmatrix == null) + if (scoreModel == null) { // Resolve substitution model - _pwmatrix = ScoreModels.getInstance().forName(pwtype); - if (_pwmatrix == null) + scoreModel = ScoreModels.getInstance().forName(pwtype); + if (scoreModel == null) { - _pwmatrix = ScoreModels.getInstance().forName("BLOSUM62"); + scoreModel = ScoreModels.getInstance().forName("BLOSUM62"); } } - dist = _pwmatrix.findDistances(seqData); - return dist; + if (scoreModel instanceof DistanceScoreModelI) + { + result = ((DistanceScoreModelI) scoreModel).findDistances(seqData); + } + else if (scoreModel instanceof SimilarityScoreModelI) + { + /* + * compute similarity and invert it to give a distance measure + */ + result = ((SimilarityScoreModelI) scoreModel) + .findSimilarities(seqData); + double maxScore = result.getMaxValue(); + result.subtractAllFrom(maxScore); + } + else + { + System.err + .println("Unexpected type of score model, can't compute distances"); + } + return result; } /** @@ -914,7 +965,7 @@ public class NJTree if ((nd.left() == null) && (nd.right() == null)) { - float dist = nd.dist; + double dist = nd.dist; if (dist > maxDistValue) { @@ -944,7 +995,7 @@ public class NJTree * * @return DOCUMENT ME! */ - public float getMaxHeight() + public double getMaxHeight() { return maxheight; } @@ -983,7 +1034,7 @@ public class NJTree * * @return DOCUMENT ME! */ - public float findHeight(SequenceNode nd) + public double findHeight(SequenceNode nd) { if (nd == null) { @@ -1033,7 +1084,7 @@ public class NJTree { ycount = 0; - float tmpdist = maxdist.dist; + double tmpdist = maxdist.dist; // New top SequenceNode sn = new SequenceNode(); @@ -1324,6 +1375,7 @@ public class NJTree * @author $author$ * @version $Revision$ */ +// TODO what does this class have that int[] doesn't have already? class Cluster { int[] value; diff --git a/src/jalview/analysis/PCA.java b/src/jalview/analysis/PCA.java index b6766c6..738da7d 100755 --- a/src/jalview/analysis/PCA.java +++ b/src/jalview/analysis/PCA.java @@ -20,9 +20,10 @@ */ package jalview.analysis; -import jalview.analysis.scoremodels.PairwiseDistanceModel; -import jalview.analysis.scoremodels.ScoreMatrix; -import jalview.analysis.scoremodels.ScoreModels; +import jalview.api.analysis.DistanceScoreModelI; +import jalview.api.analysis.ScoreModelI; +import jalview.api.analysis.SimilarityScoreModelI; +import jalview.datamodel.AlignmentView; import jalview.math.MatrixI; import java.io.PrintStream; @@ -42,56 +43,16 @@ public class PCA implements Runnable StringBuilder details = new StringBuilder(1024); - private String[] seqs; + private AlignmentView seqs; - private ScoreMatrix scoreMatrix; + private ScoreModelI scoreModel; - /** - * Creates a new PCA object. By default, uses blosum62 matrix to generate - * sequence similarity matrices - * - * @param s - * Set of amino acid sequences to perform PCA on - */ - public PCA(String[] s) - { - this(s, false); - } - - /** - * Creates a new PCA object. By default, uses blosum62 matrix to generate - * sequence similarity matrices - * - * @param s - * Set of sequences to perform PCA on - * @param nucleotides - * if true, uses standard DNA/RNA matrix for sequence similarity - * calculation. - */ - public PCA(String[] s, boolean nucleotides) - { - this(s, nucleotides, null); - } - - public PCA(String[] s, boolean nucleotides, String s_m) + public PCA(AlignmentView s, ScoreModelI sm) { this.seqs = s; - scoreMatrix = null; - String sm = s_m; - if (sm != null) - { - scoreMatrix = (ScoreMatrix) ((PairwiseDistanceModel) ScoreModels - .getInstance() - .forName(sm)).getScoreModel(); - } - if (scoreMatrix == null) - { - // either we were given a non-existent score matrix or a scoremodel that - // isn't based on a pairwise symbol score matrix - scoreMatrix = ScoreModels.getInstance().getDefaultModel(!nucleotides); - } - details.append("PCA calculation using " + sm + scoreModel = sm; + details.append("PCA calculation using " + sm.getName() + " sequence similarity matrix\n========\n\n"); } @@ -212,7 +173,7 @@ public class PCA implements Runnable + (jvCalcMode ? "Jalview variant" : "Original SeqSpace") + "\n"); - eigenvector = scoreMatrix.computePairwiseScores(seqs); + eigenvector = computeSimilarity(seqs); details.append(" --- OrigT * Orig ---- \n"); eigenvector.print(ps, "%8.2f"); @@ -254,6 +215,38 @@ public class PCA implements Runnable // + (System.currentTimeMillis() - now) + "ms")); } + /** + * Computes a pairwise similarity matrix for the given sequence regions using + * the configured score model. If the score model is a similarity model, then + * it computes the result directly. If it is a distance model, then use it to + * compute pairwise distances, and convert these to similarity scores by + * substracting from the maximum value. + * + * @param av + * @return + */ + MatrixI computeSimilarity(AlignmentView av) + { + MatrixI result = null; + if (scoreModel instanceof SimilarityScoreModelI) + { + result = ((SimilarityScoreModelI) scoreModel).findSimilarities(av); + } + else if (scoreModel instanceof DistanceScoreModelI) + { + result = ((DistanceScoreModelI) scoreModel).findDistances(av); + double maxDistance = result.getMaxValue(); + result.subtractAllFrom(maxDistance); + } + else + { + System.err + .println("Unexpected type of score model, cannot calculate similarity"); + } + + return result; + } + public void setJvCalcMode(boolean calcMode) { this.jvCalcMode = calcMode; @@ -268,6 +261,6 @@ public class PCA implements Runnable public int getHeight() { // TODO can any of seqs[] be null? - return seqs.length; + return seqs.getSequences().length; } } diff --git a/src/jalview/analysis/scoremodels/FeatureDistanceModel.java b/src/jalview/analysis/scoremodels/FeatureDistanceModel.java index 04a7b14..9245898 100644 --- a/src/jalview/analysis/scoremodels/FeatureDistanceModel.java +++ b/src/jalview/analysis/scoremodels/FeatureDistanceModel.java @@ -20,11 +20,15 @@ */ package jalview.analysis.scoremodels; -import jalview.api.analysis.DistanceModelI; +import jalview.api.AlignmentViewPanel; +import jalview.api.FeatureRenderer; +import jalview.api.analysis.DistanceScoreModelI; import jalview.api.analysis.ViewBasedAnalysisI; import jalview.datamodel.AlignmentView; import jalview.datamodel.SeqCigar; import jalview.datamodel.SequenceFeature; +import jalview.math.Matrix; +import jalview.math.MatrixI; import jalview.util.SetUtils; import java.util.HashMap; @@ -33,13 +37,14 @@ import java.util.List; import java.util.Map; import java.util.Set; -public class FeatureDistanceModel implements DistanceModelI, ViewBasedAnalysisI +public class FeatureDistanceModel implements DistanceScoreModelI, + ViewBasedAnalysisI { - jalview.api.FeatureRenderer fr; + FeatureRenderer fr; @Override - public boolean configureFromAlignmentView( - jalview.api.AlignmentViewPanel view) + public boolean configureFromAlignmentView(AlignmentViewPanel view) + { fr = view.cloneFeatureRenderer(); return true; @@ -53,16 +58,16 @@ public class FeatureDistanceModel implements DistanceModelI, ViewBasedAnalysisI * of columns processed. */ @Override - public float[][] findDistances(AlignmentView seqData) + public MatrixI findDistances(AlignmentView seqData) { List dft = fr.getDisplayedFeatureTypes(); SeqCigar[] seqs = seqData.getSequences(); int noseqs = seqs.length; int cpwidth = 0;// = seqData.getWidth(); - float[][] distance = new float[noseqs][noseqs]; + double[][] distances = new double[noseqs][noseqs]; if (dft.isEmpty()) { - return distance; + return new Matrix(distances); } // need to get real position for view position @@ -94,7 +99,7 @@ public class FeatureDistanceModel implements DistanceModelI, ViewBasedAnalysisI { int seqDistance = SetUtils.countDisjunction(sfap.get(seqs[i]), sfap.get(seqs[j])); - distance[i][j] += seqDistance; + distances[i][j] += seqDistance; } } } @@ -103,16 +108,18 @@ public class FeatureDistanceModel implements DistanceModelI, ViewBasedAnalysisI /* * normalise the distance scores (summed over columns) by the * number of visible columns used in the calculation + * and fill in the bottom half of the matrix */ + // TODO JAL-2424 cpwidth may be out by 1 - affects scores but not tree shape for (int i = 0; i < noseqs; i++) { for (int j = i + 1; j < noseqs; j++) { - distance[i][j] /= cpwidth; - distance[j][i] = distance[i][j]; + distances[i][j] /= cpwidth; + distances[j][i] = distances[i][j]; } } - return distance; + return new Matrix(distances); } /** diff --git a/src/jalview/analysis/scoremodels/PIDDistanceModel.java b/src/jalview/analysis/scoremodels/PIDDistanceModel.java index f72b0fc..154ff02 100644 --- a/src/jalview/analysis/scoremodels/PIDDistanceModel.java +++ b/src/jalview/analysis/scoremodels/PIDDistanceModel.java @@ -20,20 +20,22 @@ */ package jalview.analysis.scoremodels; -import jalview.api.analysis.DistanceModelI; +import jalview.api.analysis.DistanceScoreModelI; import jalview.datamodel.AlignmentView; +import jalview.math.Matrix; +import jalview.math.MatrixI; import jalview.util.Comparison; -public class PIDDistanceModel implements DistanceModelI +public class PIDDistanceModel implements DistanceScoreModelI { @Override - public float[][] findDistances(AlignmentView seqData) + public MatrixI findDistances(AlignmentView seqData) { String[] sequenceString = seqData .getSequenceStrings(Comparison.GAP_SPACE); int noseqs = sequenceString.length; - float[][] distance = new float[noseqs][noseqs]; + double[][] distance = new double[noseqs][noseqs]; for (int i = 0; i < (noseqs - 1); i++) { for (int j = i; j < noseqs; j++) @@ -51,7 +53,7 @@ public class PIDDistanceModel implements DistanceModelI } } } - return distance; + return new Matrix(distance); } @Override diff --git a/src/jalview/analysis/scoremodels/PairwiseDistanceModel.java b/src/jalview/analysis/scoremodels/PairwiseDistanceModel.java deleted file mode 100644 index cb9ad07..0000000 --- a/src/jalview/analysis/scoremodels/PairwiseDistanceModel.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) - * Copyright (C) $$Year-Rel$$ The Jalview Authors - * - * This file is part of Jalview. - * - * Jalview is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 - * of the License, or (at your option) any later version. - * - * Jalview is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Jalview. If not, see . - * The Jalview Authors are detailed in the 'AUTHORS' file. - */ -package jalview.analysis.scoremodels; - -import jalview.api.analysis.DistanceModelI; -import jalview.datamodel.AlignmentView; -import jalview.util.Comparison; - -public class PairwiseDistanceModel implements DistanceModelI -{ - PairwiseScoreModelI scoreModel; - - /** - * Constructor given something to provide pairwise scores for residues - * - * @param sm - */ - public PairwiseDistanceModel(PairwiseScoreModelI sm) - { - scoreModel = sm; - } - - /** - * Returns a matrix of [i][j] values representing distances between pairs of - * sequences - */ - @Override - public float[][] findDistances(AlignmentView seqData) - { - String[] sequenceString = seqData - .getSequenceStrings(Comparison.GAP_SPACE); - int noseqs = sequenceString.length; - float[][] distance = new float[noseqs][noseqs]; - - /* - * calculate similarity scores for the upper half of the matrix - * as [i, j] = the sum of pairwise scores of corresponding - * positions of sequence[i] and sequence[j] - */ - float maxscore = 0; - int end = sequenceString[0].length(); - for (int i = 0; i < (noseqs - 1); i++) - { - for (int j = i; j < noseqs; j++) - { - float score = 0; - - for (int k = 0; k < end; k++) - { - try - { - score += scoreModel.getPairwiseScore( - sequenceString[i].charAt(k), - sequenceString[j].charAt(k)); - } catch (Exception ex) - { - System.err.println("err creating " + getName() + " tree"); - ex.printStackTrace(); - } - } - - distance[i][j] = score; - - if (score > maxscore) - { - maxscore = score; - } - } - } - - /* - * subtract similarity scores from the maximum value to - * convert to a distance measure; also populate the lower - * half of the result matrix with this value - */ - // FIXME this assumes the score matrix is symmetric - it may not be? - for (int i = 0; i < (noseqs - 1); i++) - { - for (int j = i; j < noseqs; j++) - { - distance[i][j] = maxscore - distance[i][j]; - distance[j][i] = distance[i][j]; - } - } - return distance; - } - - @Override - public String getName() - { - return scoreModel.getName(); - } - - @Override - public boolean isDNA() - { - return scoreModel.isDNA(); - } - - @Override - public boolean isProtein() - { - return scoreModel.isProtein(); - } - - public PairwiseScoreModelI getScoreModel() - { - return scoreModel; - } -} diff --git a/src/jalview/analysis/scoremodels/SWDistanceModel.java b/src/jalview/analysis/scoremodels/SWDistanceModel.java index f63ee13..5e711db 100644 --- a/src/jalview/analysis/scoremodels/SWDistanceModel.java +++ b/src/jalview/analysis/scoremodels/SWDistanceModel.java @@ -21,23 +21,25 @@ package jalview.analysis.scoremodels; import jalview.analysis.AlignSeq; -import jalview.api.analysis.DistanceModelI; +import jalview.api.analysis.DistanceScoreModelI; import jalview.datamodel.AlignmentView; import jalview.datamodel.SequenceI; +import jalview.math.Matrix; +import jalview.math.MatrixI; import jalview.util.Comparison; -public class SWDistanceModel implements DistanceModelI +public class SWDistanceModel implements DistanceScoreModelI { @Override - public float[][] findDistances(AlignmentView seqData) + public MatrixI findDistances(AlignmentView seqData) { SequenceI[] sequenceString = seqData.getVisibleAlignment( Comparison.GAP_SPACE).getSequencesArray(); int noseqs = sequenceString.length; - float[][] distance = new float[noseqs][noseqs]; + double[][] distances = new double[noseqs][noseqs]; - float max = -1; + double max = -1; for (int i = 0; i < (noseqs - 1); i++) { @@ -48,11 +50,11 @@ public class SWDistanceModel implements DistanceModelI as.calcScoreMatrix(); as.traceAlignment(); as.printAlignment(System.out); - distance[i][j] = as.maxscore; + distances[i][j] = as.maxscore; - if (max < distance[i][j]) + if (max < distances[i][j]) { - max = distance[i][j]; + max = distances[i][j]; } } } @@ -61,12 +63,12 @@ public class SWDistanceModel implements DistanceModelI { for (int j = i; j < noseqs; j++) { - distance[i][j] = max - distance[i][j]; - distance[j][i] = distance[i][j]; + distances[i][j] = max - distances[i][j]; + distances[j][i] = distances[i][j]; } } - return distance; + return new Matrix(distances); } @Override diff --git a/src/jalview/analysis/scoremodels/ScoreMatrix.java b/src/jalview/analysis/scoremodels/ScoreMatrix.java index 9da28eb..efde30d 100644 --- a/src/jalview/analysis/scoremodels/ScoreMatrix.java +++ b/src/jalview/analysis/scoremodels/ScoreMatrix.java @@ -20,13 +20,25 @@ */ package jalview.analysis.scoremodels; +import jalview.api.analysis.PairwiseScoreModelI; +import jalview.api.analysis.SimilarityScoreModelI; +import jalview.datamodel.AlignmentView; import jalview.math.Matrix; import jalview.math.MatrixI; import java.util.Arrays; -public class ScoreMatrix implements PairwiseScoreModelI +public class ScoreMatrix implements SimilarityScoreModelI, + PairwiseScoreModelI { + /* + * Jalview 2.10.1 treated gaps as X (peptide) or N (nucleotide) + * for pairwise scoring; 2.10.2 uses gap score (last column) in + * score matrix (JAL-2397) + * Set this flag to true (via Groovy) for 2.10.1 behaviour + */ + private static boolean scoreGapAsAny = false; + public static final short UNMAPPED = (short) -1; private static final String BAD_ASCII_ERROR = "Unexpected character %s in getPairwiseScore"; @@ -334,7 +346,19 @@ public class ScoreMatrix implements PairwiseScoreModelI *
  • and so on
  • * */ - public MatrixI computePairwiseScores(String[] seqs) + @Override + public MatrixI findSimilarities(AlignmentView seqstrings) + { + char gapChar = scoreGapAsAny ? (seqstrings.isNa() ? 'N' : 'X') : ' '; + String[] seqs = seqstrings.getSequenceStrings(gapChar); + return findSimilarities(seqs); + } + + /** + * @param seqs + * @return + */ + protected MatrixI findSimilarities(String[] seqs) { double[][] values = new double[seqs.length][]; for (int row = 0; row < seqs.length; row++) diff --git a/src/jalview/analysis/scoremodels/ScoreModels.java b/src/jalview/analysis/scoremodels/ScoreModels.java index 043e6fb..011d8ba 100644 --- a/src/jalview/analysis/scoremodels/ScoreModels.java +++ b/src/jalview/analysis/scoremodels/ScoreModels.java @@ -1,6 +1,6 @@ package jalview.analysis.scoremodels; -import jalview.api.analysis.DistanceModelI; +import jalview.api.analysis.ScoreModelI; import jalview.io.DataSourceType; import jalview.io.FileParse; import jalview.io.ScoreMatrixFile; @@ -22,7 +22,7 @@ public class ScoreModels private static ScoreModels instance = new ScoreModels(); - private Map models; + private Map models; public static ScoreModels getInstance() { @@ -46,7 +46,7 @@ public class ScoreModels /* * using LinkedHashMap keeps models ordered as added */ - models = new LinkedHashMap(); + models = new LinkedHashMap(); BLOSUM62 = loadScoreMatrix("scoreModel/blosum62.scm"); PAM250 = loadScoreMatrix("scoreModel/pam250.scm"); loadScoreMatrix("scoreModel/seqspace.scm"); @@ -84,34 +84,24 @@ public class ScoreModels } /** - * Registers a pairwise score model - * - * @param sm - */ - public void registerScoreModel(PairwiseScoreModelI sm) - { - registerScoreModel(new PairwiseDistanceModel(sm)); - } - - /** * Answers an iterable set of the registered score models. Currently these are * returned in the order in which they were registered. * * @return */ - public Iterable getModels() + public Iterable getModels() { return models.values(); } - public DistanceModelI forName(String s) + public ScoreModelI forName(String s) { return models.get(s); } - public void registerScoreModel(DistanceModelI sm) + public void registerScoreModel(ScoreModelI sm) { - DistanceModelI sm2 = models.get(sm.getName()); + ScoreModelI sm2 = models.get(sm.getName()); if (sm2 != null) { System.err.println("Warning: replacing score model " + sm2.getName()); diff --git a/src/jalview/api/analysis/DistanceModelI.java b/src/jalview/api/analysis/DistanceModelI.java deleted file mode 100644 index 3ceeed2..0000000 --- a/src/jalview/api/analysis/DistanceModelI.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) - * Copyright (C) $$Year-Rel$$ The Jalview Authors - * - * This file is part of Jalview. - * - * Jalview is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 - * of the License, or (at your option) any later version. - * - * Jalview is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Jalview. If not, see . - * The Jalview Authors are detailed in the 'AUTHORS' file. - */ -package jalview.api.analysis; - -import jalview.datamodel.AlignmentView; - -/** - * An interface that describes classes that can compute distances between pairs - * of sequences in an alignment - */ -public interface DistanceModelI -{ - - float[][] findDistances(AlignmentView seqData); - - String getName(); - - /** - * Answers true if this model is applicable for nucleotide data (so should be - * shown in menus in that context) - * - * @return - */ - boolean isDNA(); - - /** - * Answers true if this model is applicable for peptide data (so should be - * shown in menus in that context) - * - * @return - */ - boolean isProtein(); - -} diff --git a/src/jalview/api/analysis/DistanceScoreModelI.java b/src/jalview/api/analysis/DistanceScoreModelI.java new file mode 100644 index 0000000..27003e8 --- /dev/null +++ b/src/jalview/api/analysis/DistanceScoreModelI.java @@ -0,0 +1,24 @@ +package jalview.api.analysis; + +import jalview.datamodel.AlignmentView; +import jalview.math.MatrixI; + +/** + * A sequence distance score models, that provides a method to compute distances + * between pairs of sequences + * + * @author gmcarstairs + * + */ +public interface DistanceScoreModelI extends ScoreModelI +{ + /** + * Returns a distance score for the given sequence regions, that is, a matrix + * whose value [i][j] is the distance of sequence i from sequence j by some + * measure + * + * @param seqData + * @return + */ + MatrixI findDistances(AlignmentView seqData); +} diff --git a/src/jalview/analysis/scoremodels/PairwiseScoreModelI.java b/src/jalview/api/analysis/PairwiseScoreModelI.java similarity index 95% rename from src/jalview/analysis/scoremodels/PairwiseScoreModelI.java rename to src/jalview/api/analysis/PairwiseScoreModelI.java index 8acedf0..241348c 100644 --- a/src/jalview/analysis/scoremodels/PairwiseScoreModelI.java +++ b/src/jalview/api/analysis/PairwiseScoreModelI.java @@ -1,4 +1,4 @@ -package jalview.analysis.scoremodels; +package jalview.api.analysis; /** * An interface that describes classes that can compute similarity (aka diff --git a/src/jalview/api/analysis/ScoreModelI.java b/src/jalview/api/analysis/ScoreModelI.java new file mode 100644 index 0000000..42f978c --- /dev/null +++ b/src/jalview/api/analysis/ScoreModelI.java @@ -0,0 +1,33 @@ +package jalview.api.analysis; + +import jalview.datamodel.AlignmentView; +import jalview.math.MatrixI; + +public interface ScoreModelI +{ + /** + * Answers a name for the score model, suitable for display in menus. Names + * should be unique across score models in use. + * + * @return + * @see jalview.analysis.scoremodels.ScoreModels#forName(String) + */ + String getName(); + + /** + * Answers true if this model is applicable for nucleotide data (so should be + * shown in menus in that context) + * + * @return + */ + boolean isDNA(); + + /** + * Answers true if this model is applicable for peptide data (so should be + * shown in menus in that context) + * + * @return + */ + boolean isProtein(); + +} diff --git a/src/jalview/api/analysis/SimilarityScoreModelI.java b/src/jalview/api/analysis/SimilarityScoreModelI.java new file mode 100644 index 0000000..96208a3 --- /dev/null +++ b/src/jalview/api/analysis/SimilarityScoreModelI.java @@ -0,0 +1,24 @@ +package jalview.api.analysis; + +import jalview.datamodel.AlignmentView; +import jalview.math.MatrixI; + +/** + * A class that provides a method to compute similarity scores of pairs of + * sequences + * + * @author gmcarstairs + * + */ +public interface SimilarityScoreModelI extends ScoreModelI +{ + /** + * Returns a similarity score for the given sequence regions, that is, a + * matrix whose value [i][j] is the similarity of sequence i to sequence j by + * some measure + * + * @param seqData + * @return + */ + MatrixI findSimilarities(AlignmentView seqData); +} diff --git a/src/jalview/appletgui/TreeCanvas.java b/src/jalview/appletgui/TreeCanvas.java index e30879c..7040c42 100755 --- a/src/jalview/appletgui/TreeCanvas.java +++ b/src/jalview/appletgui/TreeCanvas.java @@ -146,7 +146,7 @@ public class TreeCanvas extends Panel implements MouseListener, } public void drawNode(Graphics g, SequenceNode node, float chunk, - float scale, int width, int offx, int offy) + double scale, int width, int offx, int offy) { if (node == null) { @@ -157,8 +157,8 @@ public class TreeCanvas extends Panel implements MouseListener, { // Drawing leaf node - float height = node.height; - float dist = node.dist; + double height = node.height; + double dist = node.dist; int xstart = (int) ((height - dist) * scale) + offx; int xend = (int) (height * scale) + offx; @@ -240,8 +240,8 @@ public class TreeCanvas extends Panel implements MouseListener, drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx, offy); - float height = node.height; - float dist = node.dist; + double height = node.height; + double dist = node.dist; int xstart = (int) ((height - dist) * scale) + offx; int xend = (int) (height * scale) + offx; @@ -338,7 +338,7 @@ public class TreeCanvas extends Panel implements MouseListener, SequenceNode top = tree.getTopNode(); - float wscale = (float) (width * .8 - offx * 2) / tree.getMaxHeight(); + double wscale = (float) (width * .8 - offx * 2) / tree.getMaxHeight(); if (top.count == 0) { top.count = ((SequenceNode) top.left()).count @@ -350,7 +350,7 @@ public class TreeCanvas extends Panel implements MouseListener, } public void pickNode(Rectangle pickBox, SequenceNode node, float chunk, - float scale, int width, int offx, int offy) + double scale, int width, int offx, int offy) { if (node == null) { @@ -359,7 +359,7 @@ public class TreeCanvas extends Panel implements MouseListener, if (node.left() == null && node.right() == null) { - float height = node.height; + double height = node.height; // float dist = node.dist; // int xstart = (int) ( (height - dist) * scale) + offx; @@ -465,7 +465,7 @@ public class TreeCanvas extends Panel implements MouseListener, // for // scrollbar - float wscale = (width - labelLength - offx * 2) / tree.getMaxHeight(); + double wscale = (width - labelLength - offx * 2) / tree.getMaxHeight(); SequenceNode top = tree.getTopNode(); diff --git a/src/jalview/appletgui/TreePanel.java b/src/jalview/appletgui/TreePanel.java index 9ac6773..5b12fa6 100644 --- a/src/jalview/appletgui/TreePanel.java +++ b/src/jalview/appletgui/TreePanel.java @@ -22,7 +22,7 @@ package jalview.appletgui; import jalview.analysis.NJTree; import jalview.analysis.scoremodels.ScoreModels; -import jalview.api.analysis.DistanceModelI; +import jalview.api.analysis.ScoreModelI; import jalview.api.analysis.ViewBasedAnalysisI; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentView; @@ -237,7 +237,7 @@ public class TreePanel extends EmbmenuFrame implements ActionListener, seqs = av.getSelectionGroup().getSequencesInOrder( av.getAlignment()); } - DistanceModelI sm = ScoreModels.getInstance().forName(pwtype); + ScoreModelI sm = ScoreModels.getInstance().forName(pwtype); if (sm instanceof ViewBasedAnalysisI) { try @@ -251,13 +251,8 @@ public class TreePanel extends EmbmenuFrame implements ActionListener, + sm.getName()); q.printStackTrace(); } - tree = new NJTree(seqs, seqStrings, type, pwtype, sm, start, end); - } - else - { - tree = new NJTree(seqs, seqStrings, type, pwtype, null, start, - end); } + tree = new NJTree(seqs, seqStrings, type, pwtype, sm, start, end); } tree.reCount(tree.getTopNode()); diff --git a/src/jalview/datamodel/SequenceNode.java b/src/jalview/datamodel/SequenceNode.java index b2f054c..fa12419 100755 --- a/src/jalview/datamodel/SequenceNode.java +++ b/src/jalview/datamodel/SequenceNode.java @@ -31,13 +31,13 @@ import java.awt.Color; public class SequenceNode extends BinaryNode { /** DOCUMENT ME!! */ - public float dist; + public double dist; /** DOCUMENT ME!! */ public int count; /** DOCUMENT ME!! */ - public float height; + public double height; /** DOCUMENT ME!! */ public float ycount; @@ -178,7 +178,9 @@ public class SequenceNode extends BinaryNode { char q = name.charAt(c); if ('0' <= q && q <= '9') + { continue; + } return true; } } diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index 5f0e5d3..1162c53 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -35,7 +35,7 @@ import jalview.api.AlignmentViewPanel; import jalview.api.FeatureSettingsControllerI; import jalview.api.SplitContainerI; import jalview.api.ViewStyleI; -import jalview.api.analysis.DistanceModelI; +import jalview.api.analysis.ScoreModelI; import jalview.bin.Cache; import jalview.bin.Jalview; import jalview.commands.CommandI; @@ -5638,7 +5638,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, ColourMenuHelper.setColourSelected(colourMenu, schemeName); } - public void newTreePanel(String treeType, DistanceModelI sm) + public void newTreePanel(String treeType, ScoreModelI sm) { String scoreModelName = sm.getName(); final String ttl = TreePanel.getPanelTitle(treeType, scoreModelName); diff --git a/src/jalview/gui/PCAPanel.java b/src/jalview/gui/PCAPanel.java index d053468..3f0c37f 100644 --- a/src/jalview/gui/PCAPanel.java +++ b/src/jalview/gui/PCAPanel.java @@ -20,9 +20,10 @@ */ package jalview.gui; -import jalview.analysis.scoremodels.PairwiseDistanceModel; import jalview.analysis.scoremodels.ScoreModels; -import jalview.api.analysis.DistanceModelI; +import jalview.api.analysis.ScoreModelI; +import jalview.api.analysis.ViewBasedAnalysisI; +import jalview.bin.Cache; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; @@ -153,38 +154,42 @@ public class PCAPanel extends GPCAPanel implements Runnable, pcaModel = null; } + /** + * Repopulate the options and actions under the score model menu when it is + * selected. Options will depend on whether 'nucleotide' or 'peptide' + * modelling is selected (and also possibly on whether any additional score + * models have been added). + */ @Override - protected void scoreMatrix_menuSelected() + protected void scoreModel_menuSelected() { - scoreMatrixMenu.removeAll(); - for (DistanceModelI sm : ScoreModels.getInstance().getModels()) - { - if (sm instanceof PairwiseDistanceModel) + scoreModelMenu.removeAll(); + for (final ScoreModelI sm : ScoreModels.getInstance().getModels()) + { + final String name = sm.getName(); + // create an entry for this score matrix for use in PCA + JCheckBoxMenuItem jm = new JCheckBoxMenuItem(); + jm.setText(MessageManager.getStringOrReturn("label.score_model_", + name)); + jm.setSelected(pcaModel.getScoreModelName().equals(name)); + if ((pcaModel.isNucleotide() && sm.isDNA()) + || (!pcaModel.isNucleotide() && sm.isProtein())) { - final String name = sm.getName(); - // create an entry for this score matrix for use in PCA - JCheckBoxMenuItem jm = new JCheckBoxMenuItem(); - jm.setText(MessageManager.getStringOrReturn("label.score_model_", - name)); - jm.setSelected(pcaModel.getScore_matrix().equals(name)); - if ((pcaModel.isNucleotide() && sm.isDNA()) - || (!pcaModel.isNucleotide() && sm.isProtein())) + jm.addActionListener(new ActionListener() { - jm.addActionListener(new ActionListener() + @Override + public void actionPerformed(ActionEvent e) { - @Override - public void actionPerformed(ActionEvent e) + if (!pcaModel.getScoreModelName().equals(name)) { - if (!pcaModel.getScore_matrix().equals(name)) - { - pcaModel.setScore_matrix(name); - Thread worker = new Thread(PCAPanel.this); - worker.start(); - } + ScoreModelI sm2 = configureScoreModel(sm); + pcaModel.setScoreModel(sm2); + Thread worker = new Thread(PCAPanel.this); + worker.start(); } - }); - scoreMatrixMenu.add(jm); - } + } + }); + scoreModelMenu.add(jm); } } } @@ -258,7 +263,8 @@ public class PCAPanel extends GPCAPanel implements Runnable, if (!pcaModel.isNucleotide()) { pcaModel.setNucleotide(true); - pcaModel.setScore_matrix("DNA"); + pcaModel.setScoreModel(ScoreModels.getInstance().getDefaultModel( + false)); Thread worker = new Thread(this); worker.start(); } @@ -272,7 +278,8 @@ public class PCAPanel extends GPCAPanel implements Runnable, if (pcaModel.isNucleotide()) { pcaModel.setNucleotide(false); - pcaModel.setScore_matrix("BLOSUM62"); + pcaModel.setScoreModel(ScoreModels.getInstance() + .getDefaultModel(true)); Thread worker = new Thread(this); worker.start(); } @@ -776,4 +783,28 @@ public class PCAPanel extends GPCAPanel implements Runnable, top = t; zCombobox.setSelectedIndex(2); } + + /** + * If the score model is one that requires to get state data from the current + * view, allow it to do so + * + * @param sm + * @return + */ + protected ScoreModelI configureScoreModel(ScoreModelI sm) + { + if (sm instanceof ViewBasedAnalysisI) + { + try + { + sm = sm.getClass().newInstance(); + ((ViewBasedAnalysisI) sm).configureFromAlignmentView(ap); + } catch (Exception q) + { + Cache.log.error("Couldn't create a scoremodel instance for " + + sm.getName()); + } + } + return sm; + } } diff --git a/src/jalview/gui/TreeCanvas.java b/src/jalview/gui/TreeCanvas.java index 54eed1a..eb4253f 100755 --- a/src/jalview/gui/TreeCanvas.java +++ b/src/jalview/gui/TreeCanvas.java @@ -207,7 +207,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, * DOCUMENT ME! * @param chunk * DOCUMENT ME! - * @param scale + * @param wscale * DOCUMENT ME! * @param width * DOCUMENT ME! @@ -217,7 +217,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, * DOCUMENT ME! */ public void drawNode(Graphics g, SequenceNode node, float chunk, - float scale, int width, int offx, int offy) + double wscale, int width, int offx, int offy) { if (node == null) { @@ -227,11 +227,11 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, if ((node.left() == null) && (node.right() == null)) { // Drawing leaf node - float height = node.height; - float dist = node.dist; + double height = node.height; + double dist = node.dist; - int xstart = (int) ((height - dist) * scale) + offx; - int xend = (int) (height * scale) + offx; + int xstart = (int) ((height - dist) * wscale) + offx; + int xend = (int) (height * wscale) + offx; int ypos = (int) (node.ycount * chunk) + offy; @@ -306,16 +306,16 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, } else { - drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx, + drawNode(g, (SequenceNode) node.left(), chunk, wscale, width, offx, offy); - drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx, + drawNode(g, (SequenceNode) node.right(), chunk, wscale, width, offx, offy); - float height = node.height; - float dist = node.dist; + double height = node.height; + double dist = node.dist; - int xstart = (int) ((height - dist) * scale) + offx; - int xend = (int) (height * scale) + offx; + int xstart = (int) ((height - dist) * wscale) + offx; + int xend = (int) (height * wscale) + offx; int ypos = (int) (node.ycount * chunk) + offy; g.setColor(node.color.darker()); @@ -339,8 +339,8 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5); nodeHash.put(node, pos); - g.drawLine((int) (height * scale) + offx, ystart, - (int) (height * scale) + offx, yend); + g.drawLine((int) (height * wscale) + offx, ystart, + (int) (height * wscale) + offx, yend); String nodeLabel = ""; @@ -422,7 +422,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, SequenceNode top = tree.getTopNode(); - float wscale = (float) ((width * .8) - (offx * 2)) + double wscale = ((width * .8) - (offx * 2)) / tree.getMaxHeight(); if (top.count == 0) @@ -445,7 +445,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, * DOCUMENT ME! * @param chunk * DOCUMENT ME! - * @param scale + * @param wscale * DOCUMENT ME! * @param width * DOCUMENT ME! @@ -455,7 +455,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, * DOCUMENT ME! */ public void pickNode(Rectangle pickBox, SequenceNode node, float chunk, - float scale, int width, int offx, int offy) + double wscale, int width, int offx, int offy) { if (node == null) { @@ -464,11 +464,11 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, if ((node.left() == null) && (node.right() == null)) { - float height = node.height; - float dist = node.dist; + double height = node.height; + double dist = node.dist; - int xstart = (int) ((height - dist) * scale) + offx; - int xend = (int) (height * scale) + offx; + int xstart = (int) ((height - dist) * wscale) + offx; + int xend = (int) (height * wscale) + offx; int ypos = (int) (node.ycount * chunk) + offy; @@ -488,9 +488,9 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, } else { - pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width, + pickNode(pickBox, (SequenceNode) node.left(), chunk, wscale, width, offx, offy); - pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width, + pickNode(pickBox, (SequenceNode) node.right(), chunk, wscale, width, offx, offy); } } @@ -715,7 +715,8 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, labelLength = fm.stringWidth(longestName) + 20; // 20 allows for scrollbar - float wscale = (width - labelLength - (offx * 2)) / tree.getMaxHeight(); + double wscale = (width - labelLength - (offx * 2)) + / tree.getMaxHeight(); SequenceNode top = tree.getTopNode(); diff --git a/src/jalview/gui/TreeChooser.java b/src/jalview/gui/TreeChooser.java index 55e6650..8e2bff0 100644 --- a/src/jalview/gui/TreeChooser.java +++ b/src/jalview/gui/TreeChooser.java @@ -22,7 +22,7 @@ package jalview.gui; import jalview.analysis.NJTree; import jalview.analysis.scoremodels.ScoreModels; -import jalview.api.analysis.DistanceModelI; +import jalview.api.analysis.ScoreModelI; import jalview.util.MessageManager; import java.awt.Color; @@ -86,7 +86,7 @@ public class TreeChooser extends JPanel matrixNames = new JComboBox(); ScoreModels scoreModels = ScoreModels.getInstance(); - for (DistanceModelI sm : scoreModels.getModels()) + for (ScoreModelI sm : scoreModels.getModels()) { boolean nucleotide = af.getViewport().getAlignment().isNucleotide(); if (sm.isDNA() && nucleotide || sm.isProtein() && !nucleotide) @@ -151,7 +151,7 @@ public class TreeChooser extends JPanel frame.setClosed(true); String treeType = neighbourJoining.isSelected() ? NJTree.NEIGHBOUR_JOINING : NJTree.AVERAGE_DISTANCE; - DistanceModelI sm = ScoreModels.getInstance().forName( + ScoreModelI sm = ScoreModels.getInstance().forName( matrixNames.getSelectedItem().toString()); af.newTreePanel(treeType, sm); } catch (Exception ex) diff --git a/src/jalview/gui/TreePanel.java b/src/jalview/gui/TreePanel.java index 13e2360..0a33ea8 100755 --- a/src/jalview/gui/TreePanel.java +++ b/src/jalview/gui/TreePanel.java @@ -23,7 +23,7 @@ package jalview.gui; import jalview.analysis.AlignmentSorter; import jalview.analysis.NJTree; import jalview.analysis.scoremodels.ScoreModels; -import jalview.api.analysis.DistanceModelI; +import jalview.api.analysis.ScoreModelI; import jalview.api.analysis.ViewBasedAnalysisI; import jalview.bin.Cache; import jalview.commands.CommandI; @@ -300,26 +300,9 @@ public class TreePanel extends GTreePanel seqs = av.getSelectionGroup().getSequencesInOrder( av.getAlignment()); } - DistanceModelI sm = ScoreModels.getInstance().forName(pwtype); - if (sm instanceof ViewBasedAnalysisI) - { - try - { - sm = sm.getClass().newInstance(); - ((ViewBasedAnalysisI) sm) - .configureFromAlignmentView(treeCanvas.ap); - } catch (Exception q) - { - Cache.log.error("Couldn't create a scoremodel instance for " - + sm.getName()); - } - tree = new NJTree(seqs, seqStrings, type, pwtype, sm, start, end); - } - else - { - tree = new NJTree(seqs, seqStrings, type, pwtype, null, start, - end); - } + ScoreModelI sm = ScoreModels.getInstance().forName(pwtype); + sm = configureScoreModel(sm); + tree = new NJTree(seqs, seqStrings, type, pwtype, sm, start, end); showDistances(true); } @@ -898,4 +881,28 @@ public class TreePanel extends GTreePanel treecalcnm, smn); return ttl; } + + /** + * If the score model is one that requires to get state data from the current + * view, allow it to do so + * + * @param sm + * @return + */ + protected ScoreModelI configureScoreModel(ScoreModelI sm) + { + if (sm instanceof ViewBasedAnalysisI) + { + try + { + sm = sm.getClass().newInstance(); + ((ViewBasedAnalysisI) sm).configureFromAlignmentView(treeCanvas.ap); + } catch (Exception q) + { + Cache.log.error("Couldn't create a scoremodel instance for " + + sm.getName()); + } + } + return sm; + } } diff --git a/src/jalview/jbgui/GPCAPanel.java b/src/jalview/jbgui/GPCAPanel.java index 774641c..24961c0 100755 --- a/src/jalview/jbgui/GPCAPanel.java +++ b/src/jalview/jbgui/GPCAPanel.java @@ -52,7 +52,7 @@ public class GPCAPanel extends JInternalFrame protected JComboBox zCombobox = new JComboBox(); - protected JMenu scoreMatrixMenu = new JMenu(); + protected JMenu scoreModelMenu = new JMenu(); protected JMenu viewMenu = new JMenu(); @@ -226,14 +226,14 @@ public class GPCAPanel extends JInternalFrame { } }); - scoreMatrixMenu.setText(MessageManager + scoreModelMenu.setText(MessageManager .getString("label.select_score_model")); - scoreMatrixMenu.addMenuListener(new MenuListener() + scoreModelMenu.addMenuListener(new MenuListener() { @Override public void menuSelected(MenuEvent e) { - scoreMatrix_menuSelected(); + scoreModel_menuSelected(); } @Override @@ -312,7 +312,7 @@ public class GPCAPanel extends JInternalFrame // calcSettings.add(jvVersionSetting); // todo remove? JAL-2416 calcSettings.add(nuclSetting); calcSettings.add(protSetting); - calcSettings.add(scoreMatrixMenu); + calcSettings.add(scoreModelMenu); statusPanel.setLayout(new GridLayout()); statusBar.setFont(VERDANA_12); // statusPanel.setBackground(Color.lightGray); @@ -348,7 +348,7 @@ public class GPCAPanel extends JInternalFrame viewMenu.add(associateViewsMenu); } - protected void scoreMatrix_menuSelected() + protected void scoreModel_menuSelected() { // TODO Auto-generated method stub diff --git a/src/jalview/schemes/Blosum62ColourScheme.java b/src/jalview/schemes/Blosum62ColourScheme.java index 8ac8731..70f4910 100755 --- a/src/jalview/schemes/Blosum62ColourScheme.java +++ b/src/jalview/schemes/Blosum62ColourScheme.java @@ -20,8 +20,8 @@ */ package jalview.schemes; -import jalview.analysis.scoremodels.PairwiseScoreModelI; import jalview.analysis.scoremodels.ScoreModels; +import jalview.api.analysis.PairwiseScoreModelI; import jalview.datamodel.AnnotatedCollectionI; import jalview.datamodel.SequenceCollectionI; import jalview.datamodel.SequenceI; diff --git a/src/jalview/viewmodel/PCAModel.java b/src/jalview/viewmodel/PCAModel.java index 4ad6d48..fa4b747 100644 --- a/src/jalview/viewmodel/PCAModel.java +++ b/src/jalview/viewmodel/PCAModel.java @@ -21,7 +21,9 @@ package jalview.viewmodel; import jalview.analysis.PCA; +import jalview.analysis.scoremodels.ScoreModels; import jalview.api.RotatableCanvasI; +import jalview.api.analysis.ScoreModelI; import jalview.datamodel.AlignmentView; import jalview.datamodel.SequenceI; import jalview.datamodel.SequencePoint; @@ -30,23 +32,6 @@ import java.util.Vector; public class PCAModel { - /* - * Jalview 2.10.1 treated gaps as X (peptide) or N (nucleotide) - * for pairwise scoring; 2.10.2 uses gap score (last column) in - * score matrix (JAL-2397) - * Set this flag to true (via Groovy) for 2.10.1 behaviour - */ - private static boolean scoreGapAsAny = false; - - public PCAModel(AlignmentView seqstrings2, SequenceI[] seqs2, - boolean nucleotide2) - { - seqstrings = seqstrings2; - seqs = seqs2; - nucleotide = nucleotide2; - score_matrix = nucleotide2 ? "DNA" : "BLOSUM62"; - } - private volatile PCA pca; int top; @@ -55,20 +40,26 @@ public class PCAModel SequenceI[] seqs; - /** - * Score matrix used to calculate PC + /* + * Score model used to calculate PCA */ - String score_matrix; + ScoreModelI scoreModel; - /** - * use the identity matrix for calculating similarity between sequences. - */ private boolean nucleotide = false; private Vector points; private boolean jvCalcMode = true; + public PCAModel(AlignmentView seqstrings2, SequenceI[] seqs2, + boolean nucleotide2) + { + seqstrings = seqstrings2; + seqs = seqs2; + nucleotide = nucleotide2; + scoreModel = ScoreModels.getInstance().getDefaultModel(!nucleotide); + } + public boolean isJvCalcMode() { return jvCalcMode; @@ -76,10 +67,7 @@ public class PCAModel public void run() { - char gapChar = scoreGapAsAny ? (nucleotide ? 'N' : 'X') : ' '; - String[] sequenceStrings = seqstrings.getSequenceStrings(gapChar); - pca = new PCA(sequenceStrings, nucleotide, - score_matrix); + pca = new PCA(seqstrings, scoreModel); pca.setJvCalcMode(jvCalcMode); pca.run(); @@ -232,14 +220,14 @@ public class PCAModel jvCalcMode = state; } - public String getScore_matrix() + public String getScoreModelName() { - return score_matrix; + return scoreModel == null ? "" : scoreModel.getName(); } - public void setScore_matrix(String score_matrix) + public void setScoreModel(ScoreModelI sm) { - this.score_matrix = score_matrix; + this.scoreModel = sm; } } diff --git a/test/jalview/analysis/scoremodels/FeatureDistanceModelTest.java b/test/jalview/analysis/scoremodels/FeatureDistanceModelTest.java index 8b84829..cd6d7f5 100644 --- a/test/jalview/analysis/scoremodels/FeatureDistanceModelTest.java +++ b/test/jalview/analysis/scoremodels/FeatureDistanceModelTest.java @@ -21,6 +21,7 @@ package jalview.analysis.scoremodels; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceFeature; @@ -29,6 +30,7 @@ import jalview.gui.AlignFrame; import jalview.gui.JvOptionPane; import jalview.io.DataSourceType; import jalview.io.FileLoader; +import jalview.math.MatrixI; import java.util.Arrays; @@ -98,7 +100,7 @@ public class FeatureDistanceModelTest alf.getFeatureRenderer().findAllFeatures(true); Assert.assertEquals(alf.getFeatureRenderer().getDisplayedFeatureTypes() .size(), 3, "Number of feature types"); - Assert.assertTrue(alf.getCurrentView().areFeaturesDisplayed()); + assertTrue(alf.getCurrentView().areFeaturesDisplayed()); return alf; } @@ -107,15 +109,16 @@ public class FeatureDistanceModelTest { AlignFrame alf = getTestAlignmentFrame(); FeatureDistanceModel fsm = new FeatureDistanceModel(); - Assert.assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView() + assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView() .getAlignPanel())); alf.selectAllSequenceMenuItem_actionPerformed(null); - float[][] dm = fsm.findDistances(alf.getViewport().getAlignmentView( + MatrixI dm = fsm + .findDistances(alf.getViewport().getAlignmentView( true)); - Assert.assertTrue(dm[0][2] == 0f, + assertEquals(dm.getValue(0, 2), 0d, "FER1_MESCR (0) should be identical with RAPSA (2)"); - Assert.assertTrue(dm[0][1] > dm[0][2], + assertTrue(dm.getValue(0, 1) > dm.getValue(0, 2), "FER1_MESCR (0) should be further from SPIOL (1) than it is from RAPSA (2)"); } @@ -126,14 +129,15 @@ public class FeatureDistanceModelTest // hiding first two columns shouldn't affect the tree alf.getViewport().hideColumns(0, 1); FeatureDistanceModel fsm = new FeatureDistanceModel(); - Assert.assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView() + assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView() .getAlignPanel())); alf.selectAllSequenceMenuItem_actionPerformed(null); - float[][] dm = fsm.findDistances(alf.getViewport().getAlignmentView( + MatrixI dm = fsm + .findDistances(alf.getViewport().getAlignmentView( true)); - Assert.assertTrue(dm[0][2] == 0f, + assertEquals(dm.getValue(0, 2), 0d, "FER1_MESCR (0) should be identical with RAPSA (2)"); - Assert.assertTrue(dm[0][1] > dm[0][2], + assertTrue(dm.getValue(0, 1) > dm.getValue(0, 2), "FER1_MESCR (0) should be further from SPIOL (1) than it is from RAPSA (2)"); } @@ -145,20 +149,23 @@ public class FeatureDistanceModelTest alf.getViewport().hideColumns(3, 4); alf.getViewport().hideColumns(0, 1); FeatureDistanceModel fsm = new FeatureDistanceModel(); - Assert.assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView() + assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView() .getAlignPanel())); alf.selectAllSequenceMenuItem_actionPerformed(null); - float[][] dm = fsm.findDistances(alf.getViewport().getAlignmentView( + MatrixI dm = fsm + .findDistances(alf.getViewport().getAlignmentView( true)); - Assert.assertTrue( - dm[0][2] == 0f, + assertEquals( + dm.getValue(0, 2), + 0d, "After hiding last two columns FER1_MESCR (0) should still be identical with RAPSA (2)"); - Assert.assertTrue( - dm[0][1] == 0f, + assertEquals( + dm.getValue(0, 1), + 0d, "After hiding last two columns FER1_MESCR (0) should now also be identical with SPIOL (1)"); for (int s = 0; s < 3; s++) { - Assert.assertTrue(dm[s][3] > 0f, "After hiding last two columns " + assertTrue(dm.getValue(s, 3) > 0d, "After hiding last two columns " + alf.getViewport().getAlignment().getSequenceAt(s).getName() + "(" + s + ") should still be distinct from FER1_MAIZE (3)"); } @@ -179,7 +186,7 @@ public class FeatureDistanceModelTest SequenceFeature sf = null; sf = new SequenceFeature("disulphide bond", "", 2, 5, Float.NaN, ""); aseq.addSequenceFeature(sf); - Assert.assertTrue(sf.isContactFeature()); + assertTrue(sf.isContactFeature()); af.refreshFeatureUI(true); af.getFeatureRenderer().setAllVisible(Arrays.asList("disulphide bond")); Assert.assertEquals(af.getFeatureRenderer().getDisplayedFeatureTypes() @@ -241,19 +248,20 @@ public class FeatureDistanceModelTest alf.getFeatureRenderer().findAllFeatures(true); FeatureDistanceModel fsm = new FeatureDistanceModel(); - Assert.assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView() + assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView() .getAlignPanel())); alf.selectAllSequenceMenuItem_actionPerformed(null); - float[][] distances = fsm.findDistances(alf.getViewport() + MatrixI distances = fsm.findDistances(alf.getViewport() .getAlignmentView(true)); - assertEquals(distances.length, 2); - assertEquals(distances[0][0], 0f); - assertEquals(distances[1][1], 0f); + assertEquals(distances.width(), 2); + assertEquals(distances.height(), 2); + assertEquals(distances.getValue(0, 0), 0d); + assertEquals(distances.getValue(1, 1), 0d); // these left to fail pending resolution of // JAL-2424 (dividing score by 6, not 5) - assertEquals(distances[0][1], 1f); - assertEquals(distances[1][0], 1f); + assertEquals(distances.getValue(0, 1), 1f); + assertEquals(distances.getValue(1, 0), 1f); } } diff --git a/test/jalview/analysis/scoremodels/ScoreMatrixTest.java b/test/jalview/analysis/scoremodels/ScoreMatrixTest.java index b234d48..01de741 100644 --- a/test/jalview/analysis/scoremodels/ScoreMatrixTest.java +++ b/test/jalview/analysis/scoremodels/ScoreMatrixTest.java @@ -171,7 +171,7 @@ public class ScoreMatrixTest String[] seqs = new String[] { "FKL", "R D", "QIA", "GWC" }; ScoreMatrix sm = ScoreModels.getInstance().getBlosum62(); - MatrixI pairwise = sm.computePairwiseScores(seqs); + MatrixI pairwise = sm.findSimilarities(seqs); /* * should be NxN where N = number of sequences diff --git a/test/jalview/analysis/scoremodels/ScoreModelsTest.java b/test/jalview/analysis/scoremodels/ScoreModelsTest.java index 594adcd..c4c3edb 100644 --- a/test/jalview/analysis/scoremodels/ScoreModelsTest.java +++ b/test/jalview/analysis/scoremodels/ScoreModelsTest.java @@ -4,7 +4,10 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; -import jalview.api.analysis.DistanceModelI; +import jalview.api.analysis.DistanceScoreModelI; +import jalview.api.analysis.PairwiseScoreModelI; +import jalview.api.analysis.ScoreModelI; +import jalview.api.analysis.SimilarityScoreModelI; import java.util.Iterator; @@ -19,45 +22,52 @@ public class ScoreModelsTest @Test public void testConstructor() { - Iterator models = ScoreModels.getInstance().getModels() + Iterator models = ScoreModels.getInstance().getModels() .iterator(); assertTrue(models.hasNext()); /* * models are served in order of addition */ - DistanceModelI sm = models.next(); - assertTrue(sm instanceof PairwiseDistanceModel); + ScoreModelI sm = models.next(); + assertTrue(sm instanceof SimilarityScoreModelI); + assertTrue(sm instanceof PairwiseScoreModelI); + assertFalse(sm instanceof DistanceScoreModelI); assertEquals(sm.getName(), "BLOSUM62"); - assertEquals(((PairwiseDistanceModel) sm).getScoreModel() - .getPairwiseScore('I', 'R'), -3f); + assertEquals(((PairwiseScoreModelI) sm).getPairwiseScore('I', 'R'), -3f); sm = models.next(); - assertTrue(sm instanceof PairwiseDistanceModel); + assertTrue(sm instanceof SimilarityScoreModelI); + assertTrue(sm instanceof PairwiseScoreModelI); + assertFalse(sm instanceof DistanceScoreModelI); assertEquals(sm.getName(), "PAM250"); - assertEquals(((PairwiseDistanceModel) sm).getScoreModel() - .getPairwiseScore('R', 'C'), -4f); + assertEquals(((PairwiseScoreModelI) sm).getPairwiseScore('R', 'C'), -4f); sm = models.next(); - assertTrue(sm instanceof PairwiseDistanceModel); + assertTrue(sm instanceof SimilarityScoreModelI); + assertTrue(sm instanceof PairwiseScoreModelI); + assertFalse(sm instanceof DistanceScoreModelI); assertEquals(sm.getName(), "Identity (SeqSpace)"); - assertEquals(((PairwiseDistanceModel) sm).getScoreModel() - .getPairwiseScore('R', 'C'), 0f); - assertEquals(((PairwiseDistanceModel) sm).getScoreModel() - .getPairwiseScore('R', 'r'), 1f); + assertEquals(((PairwiseScoreModelI) sm).getPairwiseScore('R', 'C'), 0f); + assertEquals(((PairwiseScoreModelI) sm).getPairwiseScore('R', 'r'), 1f); sm = models.next(); - assertTrue(sm instanceof PairwiseDistanceModel); + assertTrue(sm instanceof SimilarityScoreModelI); + assertTrue(sm instanceof PairwiseScoreModelI); + assertFalse(sm instanceof DistanceScoreModelI); assertEquals(sm.getName(), "DNA"); - assertEquals(((PairwiseDistanceModel) sm).getScoreModel() - .getPairwiseScore('c', 'x'), 1f); + assertEquals(((PairwiseScoreModelI) sm).getPairwiseScore('c', 'x'), 1f); sm = models.next(); - assertFalse(sm instanceof PairwiseDistanceModel); + assertFalse(sm instanceof SimilarityScoreModelI); + assertFalse(sm instanceof PairwiseScoreModelI); + assertTrue(sm instanceof DistanceScoreModelI); assertEquals(sm.getName(), "Sequence Feature Similarity"); sm = models.next(); - assertFalse(sm instanceof PairwiseDistanceModel); + assertFalse(sm instanceof SimilarityScoreModelI); + assertFalse(sm instanceof PairwiseScoreModelI); + assertTrue(sm instanceof DistanceScoreModelI); assertEquals(sm.getName(), "PID"); } @@ -92,17 +102,11 @@ public class ScoreModelsTest */ protected void printAllMatrices(boolean asHtml) { - for (DistanceModelI dm : ScoreModels.getInstance().getModels()) + for (ScoreModelI sm : ScoreModels.getInstance().getModels()) { - if (dm instanceof PairwiseDistanceModel) + if (sm instanceof ScoreMatrix) { - PairwiseScoreModelI psm = ((PairwiseDistanceModel) dm) - .getScoreModel(); - if (psm instanceof ScoreMatrix) - { - ScoreMatrix sm = (ScoreMatrix) psm; - System.out.println(sm.outputMatrix(asHtml)); - } + System.out.println(((ScoreMatrix) sm).outputMatrix(asHtml)); } } } diff --git a/test/jalview/util/ComparisonTest.java b/test/jalview/util/ComparisonTest.java index f955879..ec1779b 100644 --- a/test/jalview/util/ComparisonTest.java +++ b/test/jalview/util/ComparisonTest.java @@ -129,12 +129,14 @@ public class ComparisonTest int length = seq1.length(); // match gap-residue, match gap-gap: 9/10 identical + // TODO should gap-gap be included in a PID score? JAL-791 assertEquals(90f, Comparison.PID(seq1, seq2, 0, length, true, false), 0.001f); // overloaded version of the method signature above: assertEquals(90f, Comparison.PID(seq1, seq2), 0.001f); // don't match gap-residue, match gap-gap: 7/10 identical + // TODO should gap-gap be included in a PID score? assertEquals(70f, Comparison.PID(seq1, seq2, 0, length, false, false), 0.001f); } -- 1.7.10.2