From: Jim Procter Date: Thu, 16 May 2024 07:07:17 +0000 (+0100) Subject: Merge branch 'develop' into feature/JAL-4159_pasimap X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=37c507c5ed74c4b1564556a19d0bc651f8dccc2d;hp=ef7125ccaa49531e0f0930955af7a1710f6c4372;p=jalview.git Merge branch 'develop' into feature/JAL-4159_pasimap doesn't seem to have apache-math present Conflicts: build.gradle --- diff --git a/.gitignore b/.gitignore index 59c4a99..4a81616 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ TESTNG /jalviewApplet.jar /benchmarking/lib *.class +*.patch /site /site-resources /libjs diff --git a/.zip b/.zip new file mode 100644 index 0000000..4de119f Binary files /dev/null and b/.zip differ diff --git a/appletlib.zip b/appletlib.zip new file mode 100644 index 0000000..f089e5e Binary files /dev/null and b/appletlib.zip differ diff --git a/build.diff b/build.diff new file mode 100644 index 0000000..8dc92f7 --- /dev/null +++ b/build.diff @@ -0,0 +1,531 @@ +--- build.gradle 2023-07-02 09:21:09.216542293 +0200 ++++ ../jalview_new/build.gradle 2023-05-15 19:20:46.892861180 +0200 +@@ -45,8 +45,8 @@ + id 'java' + id 'application' + id 'eclipse' +- id "com.diffplug.gradle.spotless" version "3.28.0" +- id 'com.github.johnrengelman.shadow' version '4.0.3' ++ id "com.diffplug.spotless" version "6.18.0" //.gradle.spotless" "3.28.0" ++ id 'com.github.johnrengelman.shadow' version '8.1.1' // was 4.0.3 + id 'com.install4j.gradle' version '9.0.6' + id 'com.dorongold.task-tree' version '2.1.0' // only needed to display task dependency tree with gradle task1 [task2 ...] taskTree + id 'com.palantir.git-version' version '0.13.0' apply false +@@ -183,6 +183,7 @@ + testDir = string("${jalviewDir}/${bareTestSourceDir}") + + classesDir = string("${jalviewDir}/${classes_dir}") ++ destinationDirectory = file(classesDir) + + // clover + useClover = clover.equals("true") +@@ -547,14 +548,14 @@ + main { + java { + srcDirs sourceDir +- outputDir = file(classesDir) ++ destinationDirectory = file(classesDir) + } + + resources { + srcDirs = [ resourcesBuildDir, docBuildDir, helpBuildDir ] + } + +- compileClasspath = files(sourceSets.main.java.outputDir) ++ compileClasspath = files(sourceSets.main.java.destinationDirectory) + compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"]) + + runtimeClasspath = compileClasspath +@@ -564,14 +565,14 @@ + clover { + java { + srcDirs cloverInstrDir +- outputDir = cloverClassesDir ++ destinationDirectory = cloverClassesDir + } + + resources { + srcDirs = sourceSets.main.resources.srcDirs + } + +- compileClasspath = files( sourceSets.clover.java.outputDir ) ++ compileClasspath = files( sourceSets.clover.java.destinationDirectory ) + //compileClasspath += files( testClassesDir ) + compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"]) + compileClasspath += fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"]) +@@ -583,14 +584,14 @@ + test { + java { + srcDirs testSourceDir +- outputDir = file(testClassesDir) ++ destinationDirectory = file(testClassesDir) + } + + resources { + srcDirs = useClover ? sourceSets.clover.resources.srcDirs : sourceSets.main.resources.srcDirs + } + +- compileClasspath = files( sourceSets.test.java.outputDir ) ++ compileClasspath = files( sourceSets.test.java.destinationDirectory ) + compileClasspath += useClover ? sourceSets.clover.compileClasspath : sourceSets.main.compileClasspath + compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"]) + +@@ -615,7 +616,7 @@ + } + + classpath { +- //defaultOutputDir = sourceSets.main.java.outputDir ++ //defaultOutputDir = sourceSets.main.java.destinationDirectory + configurations.each{ c-> + if (c.isCanBeResolved()) { + minusConfigurations += [c] +@@ -654,7 +655,7 @@ + HashMap alreadyAddedLibPath = new HashMap<>(); + + sourceSets.main.compileClasspath.findAll { it.name.endsWith(".jar") }.any { +- //don't want to add outputDir as eclipse is using its own output dir in bin/main ++ //don't want to add destinationDirectory as eclipse is using its own output dir in bin/main + if (it.isDirectory() || ! it.exists()) { + // don't add dirs to classpath, especially if they don't exist + return false // groovy "continue" in .any closure +@@ -674,7 +675,7 @@ + } + + sourceSets.test.compileClasspath.findAll { it.name.endsWith(".jar") }.any { +- //no longer want to add outputDir as eclipse is using its own output dir in bin/main ++ //no longer want to add destinationDirectory as eclipse is using its own output dir in bin/main + if (it.isDirectory() || ! it.exists()) { + // don't add dirs to classpath + return false // groovy "continue" in .any closure +@@ -1051,7 +1052,7 @@ + + clean { + doFirst { +- delete sourceSets.main.java.outputDir ++ delete sourceSets.main.java.destinationDirectory + } + } + +@@ -1059,7 +1060,7 @@ + cleanTest { + dependsOn cleanClover + doFirst { +- delete sourceSets.test.java.outputDir ++ delete sourceSets.test.java.destinationDirectory + } + } + +@@ -1151,7 +1152,7 @@ + + task copyDocs(type: Copy) { + def inputDir = "${jalviewDir}/${doc_dir}" +- def outputDir = "${docBuildDir}/${doc_dir}" ++ def destinationDirectory = "${docBuildDir}/${doc_dir}" + from(inputDir) { + include('**/*.txt') + include('**/*.md') +@@ -1172,10 +1173,10 @@ + exclude('**/*.html') + exclude('**/*.xml') + } +- into outputDir ++ into destinationDirectory + + inputs.dir(inputDir) +- outputs.dir(outputDir) ++ outputs.dir(destinationDirectory) + } + + +@@ -1240,15 +1241,15 @@ + } + if (inFrontMatter) { + def m = null +- if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/) { ++ if (m == line =~ /^date:\s*(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/) { + map["date"] = new Date().parse("yyyy-MM-dd HH:mm:ss", m[0][1]) +- } else if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2})/) { ++ } else if (m == line =~ /^date:\s*(\d{4}-\d{2}-\d{2})/) { + map["date"] = new Date().parse("yyyy-MM-dd", m[0][1]) +- } else if (m = line =~ /^channel:\s*(\S+)/) { ++ } else if (m == line =~ /^channel:\s*(\S+)/) { + map["channel"] = m[0][1] +- } else if (m = line =~ /^version:\s*(\S+)/) { ++ } else if (m == line =~ /^version:\s*(\S+)/) { + map["version"] = m[0][1] +- } else if (m = line =~ /^\s*([^:]+)\s*:\s*(\S.*)/) { ++ } else if (m == line =~ /^\s*([^:]+)\s*:\s*(\S.*)/) { + map[ m[0][1] ] = m[0][2] + } + if (dateOnly && map["date"] != null) { +@@ -1307,7 +1308,7 @@ + def inSection = false + changes.eachLine { line -> + def m = null +- if (m = line =~ /^##([^#].*)$/) { ++ if (m == line =~ /^##([^#].*)$/) { + if (inSection) { + changesHugo += "\n\n" + } +@@ -1317,7 +1318,7 @@ + section = section.replaceAll(/[^a-z0-9_\-]/, "") + changesHugo += "
\n\n" + inSection = true +- } else if (m = line =~ /^(\s*-\s*)(.*?)()?\s*$/) { ++ } else if (m == line =~ /^(\s*-\s*)(.*?)()?\s*$/) { + def comment = m[0][2].trim() + if (comment != "") { + comment = comment.replaceAll('"', """) +@@ -1387,7 +1388,7 @@ + def sectionName = null + content.eachLine { line -> + def m = null +- if (m = line =~ /^##([^#].*)$/) { ++ if (m == line =~ /^##([^#].*)$/) { + if (sectionName != null) { + sections[sectionName] = sectionContent + sectionName = null +@@ -1410,7 +1411,7 @@ + + task copyHelp(type: Copy) { + def inputDir = helpSourceDir +- def outputDir = "${helpBuildDir}/${help_dir}" ++ def destinationDirectory = "${helpBuildDir}/${help_dir}" + from(inputDir) { + include('**/*.txt') + include('**/*.md') +@@ -1435,14 +1436,15 @@ + exclude('**/*.xml') + exclude('**/*.jhm') + } +- into outputDir ++ into destinationDirectory + + inputs.dir(inputDir) + outputs.files(helpFile) +- outputs.dir(outputDir) ++ outputs.dir(destinationDirectory) + } + + ++/* + task releasesTemplates { + group "help" + description "Recreate whatsNew.html and releases.html from markdown files and templates in help" +@@ -1519,9 +1521,9 @@ + def lm = null + def rContentProcessed = "" + rContent.eachLine { line -> +- if (lm = line =~ /^(\s*-)(\s*)(.*)$/) { ++ if (lm == line =~ /^(\s*-)(\s*)(.*)$/) { + line = "${lm[0][1]}${lm[0][3]}${lm[0][2]}" +- } else if (lm = line =~ /^###([^#]+.*)$/) { ++ } else if (lm == line =~ /^###([^#]+.*)$/) { + line = "_${lm[0][1].trim()}_" + } + rContentProcessed += line + "\n" +@@ -1579,13 +1581,14 @@ + outputs.file(whatsnewHtmlFile) + } + ++*/ + + task copyResources(type: Copy) { + group = "build" + description = "Copy (and make text substitutions in) the resources dir to the build area" + + def inputDir = resourceDir +- def outputDir = resourcesBuildDir ++ def destinationDirectory = resourcesBuildDir + from(inputDir) { + include('**/*.txt') + include('**/*.md') +@@ -1606,10 +1609,10 @@ + exclude('**/*.html') + exclude('**/*.xml') + } +- into outputDir ++ into destinationDirectory + + inputs.dir(inputDir) +- outputs.dir(outputDir) ++ outputs.dir(destinationDirectory) + } + + task copyChannelResources(type: Copy) { +@@ -1618,16 +1621,17 @@ + description = "Copy the channel resources dir to the build resources area" + + def inputDir = "${channelDir}/${resource_dir}" +- def outputDir = resourcesBuildDir ++ def destinationDirectory = resourcesBuildDir + from inputDir +- into outputDir ++ into destinationDirectory + + inputs.dir(inputDir) +- outputs.dir(outputDir) ++ outputs.dir(destinationDirectory) + } + + task createBuildProperties(type: WriteProperties) { + dependsOn copyResources ++ dependsOn copyChannelResources + group = "build" + description = "Create the ${buildProperties} file" + +@@ -1651,6 +1655,7 @@ + + task buildIndices(type: JavaExec) { + dependsOn copyHelp ++ //dependsOn releasesTemplates + classpath = sourceSets.main.compileClasspath + main = "com.sun.java.help.search.Indexer" + workingDir = "${helpBuildDir}/${help_dir}" +@@ -1678,15 +1683,25 @@ + dependsOn buildResources + dependsOn copyDocs + dependsOn copyHelp +- dependsOn releasesTemplates ++ //dependsOn releasesTemplates + dependsOn convertMdFiles + dependsOn buildIndices + } + + ++// random block of dependencies + compileJava.dependsOn prepare + run.dependsOn compileJava + //run.dependsOn prepare ++compileTestJava.dependsOn compileJava // ++compileTestJava.dependsOn buildIndices // ++processResources.dependsOn copyChannelResources // ++processResources.dependsOn copyResources // ++processResources.dependsOn createBuildProperties // ++processResources.dependsOn copyDocs // ++processResources.dependsOn convertMdFiles // ++processResources.dependsOn copyHelp // ++processResources.dependsOn buildIndices // + + + //testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir +@@ -1731,6 +1746,7 @@ + } + + ++/* + task compileLinkCheck(type: JavaCompile) { + options.fork = true + classpath = files("${jalviewDir}/${utils_dir}") +@@ -1765,6 +1781,7 @@ + inputs.dir(helpBuildDir) + outputs.file(helpLinksCheckerOutFile) + } ++*/ + + + // import the pubhtmlhelp target +@@ -1779,10 +1796,14 @@ + } + } + ++// block of dependencies ++//compileTestJava.dependsOn compileLinkCheck // ++//copyChannelResources.dependsOn compileLinkCheck // ++//convertMdFiles.dependsOn compileLinkCheck // + + jar { + dependsOn prepare +- dependsOn linkCheck ++ dependsOn //linkCheck + + manifest { + attributes "Main-Class": main_class, +@@ -1792,8 +1813,8 @@ + "Implementation-Version": JALVIEW_VERSION + } + +- def outputDir = "${jalviewDir}/${package_dir}" +- destinationDirectory = file(outputDir) ++ def destinationDirectory = "${jalviewDir}/${package_dir}" ++ destinationDirectory = file(destinationDirectory) + archiveFileName = rootProject.name+".jar" + duplicatesStrategy "EXCLUDE" + +@@ -1804,11 +1825,11 @@ + exclude "**/*.jar" + exclude "**/*.jar.*" + +- inputs.dir(sourceSets.main.java.outputDir) ++ inputs.dir(sourceSets.main.java.destinationDirectory) + sourceSets.main.resources.srcDirs.each{ dir -> + inputs.dir(dir) + } +- outputs.file("${outputDir}/${archiveFileName}") ++ outputs.file("${destinationDirectory}/${archiveFileName}") + } + + +@@ -1867,7 +1888,7 @@ + + mainClassName = shadow_jar_main_class + mergeServiceFiles() +- classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION ++ archiveClassifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION + minimize() + } + +@@ -2922,10 +2943,10 @@ + + def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar") + inputFiles += file("${jalviewDir}/${jalviewjsJ2sPlugin}") +- def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ++ def destinationDirectory = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" + + from inputFiles +- into outputDir ++ into destinationDirectory + } + + +@@ -3033,13 +3054,13 @@ + dependsOn jalviewjsTransferUnzipAllLibs + def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}") + inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}") +- def outputDir = "${jalviewDir}/${jalviewjsSiteDir}" ++ def destinationDirectory = "${jalviewDir}/${jalviewjsSiteDir}" + + from inputFiles +- into outputDir ++ into destinationDirectory + def outputFiles = [] + rename { filename -> +- outputFiles += "${outputDir}/${filename}" ++ outputFiles += "${destinationDirectory}/${filename}" + null + } + preserve { +@@ -3058,13 +3079,13 @@ + dependsOn buildResources + + def inputFiles = fileTree(dir: resourcesBuildDir) +- def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}" ++ def destinationDirectory = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}" + + from inputFiles +- into outputDir ++ into destinationDirectory + def outputFiles = [] + rename { filename -> +- outputFiles += "${outputDir}/${filename}" ++ outputFiles += "${destinationDirectory}/${filename}" + null + } + preserve { +@@ -3077,13 +3098,13 @@ + + task jalviewjsSyncSiteResources (type: Sync) { + def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}") +- def outputDir = "${jalviewDir}/${jalviewjsSiteDir}" ++ def destinationDirectory = "${jalviewDir}/${jalviewjsSiteDir}" + + from inputFiles +- into outputDir ++ into destinationDirectory + def outputFiles = [] + rename { filename -> +- outputFiles += "${outputDir}/${filename}" ++ outputFiles += "${destinationDirectory}/${filename}" + null + } + preserve { +@@ -3097,13 +3118,13 @@ + task jalviewjsSyncBuildProperties (type: Sync) { + dependsOn createBuildProperties + def inputFiles = [file(buildProperties)] +- def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}" ++ def destinationDirectory = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}" + + from inputFiles +- into outputDir ++ into destinationDirectory + def outputFiles = [] + rename { filename -> +- outputFiles += "${outputDir}/${filename}" ++ outputFiles += "${destinationDirectory}/${filename}" + null + } + preserve { +@@ -3322,7 +3343,7 @@ + def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_j2s_subdir}" + def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteLibDir}/${jalviewjs_j2s_subdir}" + def jsDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_js_subdir}" +- def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core" ++ def destinationDirectory = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core" + def prefixFile = "${jsDir}/core/coretop2.js" + def suffixFile = "${jsDir}/core/corebottom2.js" + +@@ -3366,8 +3387,8 @@ + } + def list = fileTree(dir: j2sDir, includes: filelist) + +- def jsfile = "${outputDir}/core${name}.js" +- def zjsfile = "${outputDir}/core${name}.z.js" ++ def jsfile = "${destinationDirectory}/core${name}.js" ++ def zjsfile = "${destinationDirectory}/core${name}.z.js" + + jalviewjsCoreClasslists += [ + 'jsfile': jsfile, +@@ -3385,8 +3406,8 @@ + // _stevesoft core. add any cores without a classlist here (and the inputs and outputs) + def stevesoftClasslistName = "_stevesoft" + def stevesoftClasslist = [ +- 'jsfile': "${outputDir}/core${stevesoftClasslistName}.js", +- 'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js", ++ 'jsfile': "${destinationDirectory}/core${stevesoftClasslistName}.js", ++ 'zjsfile': "${destinationDirectory}/core${stevesoftClasslistName}.z.js", + 'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"), + 'name': stevesoftClasslistName + ] +@@ -3419,8 +3440,8 @@ + ] + ) + def allClasslist = [ +- 'jsfile': "${outputDir}/core${allClasslistName}.js", +- 'zjsfile': "${outputDir}/core${allClasslistName}.z.js", ++ 'jsfile': "${destinationDirectory}/core${allClasslistName}.js", ++ 'zjsfile': "${destinationDirectory}/core${allClasslistName}.z.js", + 'list': allJsFiles, + 'name': allClasslistName + ] +@@ -3473,11 +3494,11 @@ + dependsOn jalviewjsBuildAllCores + def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}" + def inputFile = file(inputFileName) +- def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}" ++ def destinationDirectory = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}" + + def outputFiles = [] + jalviewjsCoreClasslists.each { cl -> +- def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html" ++ def outputFile = "${destinationDirectory}/${jalviewjsJalviewTemplateName}_${cl.name}.html" + cl['outputfile'] = outputFile + outputFiles += outputFile + } +@@ -3496,13 +3517,13 @@ + dependsOn jalviewjsBuildAllCores + dependsOn jalviewjsPublishCoreTemplates + def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}") +- def outputDir = "${jalviewDir}/${jalviewjsSiteDir}" ++ def destinationDirectory = "${jalviewDir}/${jalviewjsSiteDir}" + + from inputFiles +- into outputDir ++ into destinationDirectory + def outputFiles = [] + rename { filename -> +- outputFiles += "${outputDir}/${filename}" ++ outputFiles += "${destinationDirectory}/${filename}" + null + } + preserve { diff --git a/build.gradle b/build.gradle index 6df3ec9..90686b3 100644 --- a/build.gradle +++ b/build.gradle @@ -193,6 +193,7 @@ ext { testDir = string("${jalviewDir}/${bareTestSourceDir}") classesDir = string("${jalviewDir}/${classes_dir}") + destinationDirectory = file(classesDir) // clover useClover = clover.equals("true") @@ -590,14 +591,14 @@ sourceSets { main { java { srcDirs sourceDir - outputDir = file(classesDir) + destinationDirectory = file(classesDir) } resources { srcDirs = [ resourcesBuildDir, docBuildDir, helpBuildDir ] } - compileClasspath = files(sourceSets.main.java.outputDir) + compileClasspath = files(sourceSets.main.java.destinationDirectory) compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"]) runtimeClasspath = compileClasspath @@ -607,14 +608,14 @@ sourceSets { clover { java { srcDirs cloverInstrDir - outputDir = cloverClassesDir + destinationDirectory = cloverClassesDir } resources { srcDirs = sourceSets.main.resources.srcDirs } - compileClasspath = files( sourceSets.clover.java.outputDir ) + compileClasspath = files( sourceSets.clover.java.destinationDirectory ) //compileClasspath += files( testClassesDir ) compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"]) compileClasspath += fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"]) @@ -626,14 +627,14 @@ sourceSets { test { java { srcDirs testSourceDir - outputDir = file(testClassesDir) + destinationDirectory = file(testClassesDir) } resources { srcDirs = useClover ? sourceSets.clover.resources.srcDirs : sourceSets.main.resources.srcDirs } - compileClasspath = files( sourceSets.test.java.outputDir ) + compileClasspath = files( sourceSets.test.java.destinationDirectory ) compileClasspath += useClover ? sourceSets.clover.compileClasspath : sourceSets.main.compileClasspath compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"]) @@ -658,7 +659,7 @@ eclipse { } classpath { - //defaultOutputDir = sourceSets.main.java.outputDir + //defaultOutputDir = sourceSets.main.java.destinationDirectory configurations.each{ c-> if (c.isCanBeResolved()) { minusConfigurations += [c] @@ -697,7 +698,7 @@ eclipse { HashMap alreadyAddedLibPath = new HashMap<>(); sourceSets.main.compileClasspath.findAll { it.name.endsWith(".jar") }.any { - //don't want to add outputDir as eclipse is using its own output dir in bin/main + //don't want to add destinationDirectory as eclipse is using its own output dir in bin/main if (it.isDirectory() || ! it.exists()) { // don't add dirs to classpath, especially if they don't exist return false // groovy "continue" in .any closure @@ -717,7 +718,7 @@ eclipse { } sourceSets.test.compileClasspath.findAll { it.name.endsWith(".jar") }.any { - //no longer want to add outputDir as eclipse is using its own output dir in bin/main + //no longer want to add destinationDirectory as eclipse is using its own output dir in bin/main if (it.isDirectory() || ! it.exists()) { // don't add dirs to classpath return false // groovy "continue" in .any closure @@ -1094,7 +1095,7 @@ compileTestJava { clean { doFirst { - delete sourceSets.main.java.outputDir + delete sourceSets.main.java.destinationDirectory } } @@ -1102,7 +1103,7 @@ clean { cleanTest { dependsOn cleanClover doFirst { - delete sourceSets.test.java.outputDir + delete sourceSets.test.java.destinationDirectory } } @@ -1194,7 +1195,7 @@ def convertMdToHtml (FileTree mdFiles, File cssFile) { task copyDocs(type: Copy) { def inputDir = "${jalviewDir}/${doc_dir}" - def outputDir = "${docBuildDir}/${doc_dir}" + def destinationDirectory = "${docBuildDir}/${doc_dir}" from(inputDir) { include('**/*.txt') include('**/*.md') @@ -1215,10 +1216,10 @@ task copyDocs(type: Copy) { exclude('**/*.html') exclude('**/*.xml') } - into outputDir + into destinationDirectory inputs.dir(inputDir) - outputs.dir(outputDir) + outputs.dir(destinationDirectory) } @@ -1283,15 +1284,15 @@ def mdFileComponents(File mdFile, def dateOnly=false) { } if (inFrontMatter) { def m = null - if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/) { + if (m == line =~ /^date:\s*(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/) { map["date"] = new Date().parse("yyyy-MM-dd HH:mm:ss", m[0][1]) - } else if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2})/) { + } else if (m == line =~ /^date:\s*(\d{4}-\d{2}-\d{2})/) { map["date"] = new Date().parse("yyyy-MM-dd", m[0][1]) - } else if (m = line =~ /^channel:\s*(\S+)/) { + } else if (m == line =~ /^channel:\s*(\S+)/) { map["channel"] = m[0][1] - } else if (m = line =~ /^version:\s*(\S+)/) { + } else if (m == line =~ /^version:\s*(\S+)/) { map["version"] = m[0][1] - } else if (m = line =~ /^\s*([^:]+)\s*:\s*(\S.*)/) { + } else if (m == line =~ /^\s*([^:]+)\s*:\s*(\S.*)/) { map[ m[0][1] ] = m[0][2] } if (dateOnly && map["date"] != null) { @@ -1350,7 +1351,7 @@ task hugoTemplates { def inSection = false changes.eachLine { line -> def m = null - if (m = line =~ /^##([^#].*)$/) { + if (m == line =~ /^##([^#].*)$/) { if (inSection) { changesHugo += "
\n\n" } @@ -1360,7 +1361,7 @@ task hugoTemplates { section = section.replaceAll(/[^a-z0-9_\-]/, "") changesHugo += "
\n\n" inSection = true - } else if (m = line =~ /^(\s*-\s*)(.*?)()?\s*$/) { + } else if (m == line =~ /^(\s*-\s*)(.*?)()?\s*$/) { def comment = m[0][2].trim() if (comment != "") { comment = comment.replaceAll('"', """) @@ -1430,7 +1431,7 @@ def getMdSections(String content) { def sectionName = null content.eachLine { line -> def m = null - if (m = line =~ /^##([^#].*)$/) { + if (m == line =~ /^##([^#].*)$/) { if (sectionName != null) { sections[sectionName] = sectionContent sectionName = null @@ -1453,7 +1454,7 @@ def getMdSections(String content) { task copyHelp(type: Copy) { def inputDir = helpSourceDir - def outputDir = "${helpBuildDir}/${help_dir}" + def destinationDirectory = "${helpBuildDir}/${help_dir}" from(inputDir) { include('**/*.txt') include('**/*.md') @@ -1478,14 +1479,15 @@ task copyHelp(type: Copy) { exclude('**/*.xml') exclude('**/*.jhm') } - into outputDir + into destinationDirectory inputs.dir(inputDir) outputs.files(helpFile) - outputs.dir(outputDir) + outputs.dir(destinationDirectory) } +/* task releasesTemplates { group "help" description "Recreate whatsNew.html and releases.html from markdown files and templates in help" @@ -1562,9 +1564,9 @@ task releasesTemplates { def lm = null def rContentProcessed = "" rContent.eachLine { line -> - if (lm = line =~ /^(\s*-)(\s*)(.*)$/) { + if (lm == line =~ /^(\s*-)(\s*)(.*)$/) { line = "${lm[0][1]}${lm[0][3]}${lm[0][2]}" - } else if (lm = line =~ /^###([^#]+.*)$/) { + } else if (lm == line =~ /^###([^#]+.*)$/) { line = "_${lm[0][1].trim()}_" } rContentProcessed += line + "\n" @@ -1622,13 +1624,14 @@ task releasesTemplates { outputs.file(whatsnewHtmlFile) } +*/ task copyResources(type: Copy) { group = "build" description = "Copy (and make text substitutions in) the resources dir to the build area" def inputDir = resourceDir - def outputDir = resourcesBuildDir + def destinationDirectory = resourcesBuildDir from(inputDir) { include('**/*.txt') include('**/*.md') @@ -1649,10 +1652,10 @@ task copyResources(type: Copy) { exclude('**/*.html') exclude('**/*.xml') } - into outputDir + into destinationDirectory inputs.dir(inputDir) - outputs.dir(outputDir) + outputs.dir(destinationDirectory) } task copyChannelResources(type: Copy) { @@ -1661,7 +1664,7 @@ task copyChannelResources(type: Copy) { description = "Copy the channel resources dir to the build resources area" def inputDir = "${channelDir}/${resource_dir}" - def outputDir = resourcesBuildDir + def destinationDirectory = resourcesBuildDir from(inputDir) { include(channel_props) filter(ReplaceTokens, @@ -1675,14 +1678,15 @@ task copyChannelResources(type: Copy) { from(inputDir) { exclude(channel_props) } - into outputDir + into destinationDirectory inputs.dir(inputDir) - outputs.dir(outputDir) + outputs.dir(destinationDirectory) } task createBuildProperties(type: WriteProperties) { dependsOn copyResources + dependsOn copyChannelResources group = "build" description = "Create the ${buildProperties} file" @@ -1706,6 +1710,7 @@ task createBuildProperties(type: WriteProperties) { task buildIndices(type: JavaExec) { dependsOn copyHelp + //dependsOn releasesTemplates classpath = sourceSets.main.compileClasspath main = "com.sun.java.help.search.Indexer" workingDir = "${helpBuildDir}/${help_dir}" @@ -1733,18 +1738,25 @@ task prepare { dependsOn buildResources dependsOn copyDocs dependsOn copyHelp - dependsOn releasesTemplates + //dependsOn releasesTemplates dependsOn convertMdFiles dependsOn buildIndices } +// random block of dependencies compileJava.dependsOn prepare run.dependsOn compileJava -compileTestJava.dependsOn compileJava - - - +//run.dependsOn prepare +compileTestJava.dependsOn compileJava // +compileTestJava.dependsOn buildIndices // +processResources.dependsOn copyChannelResources // +processResources.dependsOn copyResources // +processResources.dependsOn createBuildProperties // +processResources.dependsOn copyDocs // +processResources.dependsOn convertMdFiles // +processResources.dependsOn copyHelp // +processResources.dependsOn buildIndices // test { group = "Verification" description = "Runs all testTaskN tasks)" @@ -2037,6 +2049,7 @@ private static void printResults(allResults) { /* END of test tasks results summary */ +/* task compileLinkCheck(type: JavaCompile) { options.fork = true classpath = files("${jalviewDir}/${utils_dir}") @@ -2071,6 +2084,7 @@ task linkCheck(type: JavaExec) { inputs.dir(helpBuildDir) outputs.file(helpLinksCheckerOutFile) } +*/ // import the pubhtmlhelp target @@ -2085,10 +2099,14 @@ task cleanPackageDir(type: Delete) { } } +// block of dependencies +//compileTestJava.dependsOn compileLinkCheck // +//copyChannelResources.dependsOn compileLinkCheck // +//convertMdFiles.dependsOn compileLinkCheck // jar { dependsOn prepare - dependsOn linkCheck + dependsOn //linkCheck manifest { attributes "Main-Class": main_class, @@ -2098,8 +2116,8 @@ jar { "Implementation-Version": JALVIEW_VERSION } - def outputDir = "${jalviewDir}/${package_dir}" - destinationDirectory = file(outputDir) + def destinationDirectory = "${jalviewDir}/${package_dir}" + destinationDirectory = file(destinationDirectory) archiveFileName = rootProject.name+".jar" duplicatesStrategy "EXCLUDE" @@ -2110,11 +2128,11 @@ jar { exclude "**/*.jar" exclude "**/*.jar.*" - inputs.dir(sourceSets.main.java.outputDir) + inputs.dir(sourceSets.main.java.destinationDirectory) sourceSets.main.resources.srcDirs.each{ dir -> inputs.dir(dir) } - outputs.file("${outputDir}/${archiveFileName}") + outputs.file("${destinationDirectory}/${archiveFileName}") } @@ -2207,7 +2225,7 @@ shadowJar { // this mainClassName is mandatory but gets ignored due to manifest created in doFirst{}. Set the Main-Class as an attribute in launcherJar instead mainClassName = shadow_jar_main_class mergeServiceFiles() - classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION + archiveClassifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION minimize() } @@ -3388,10 +3406,10 @@ task jalviewjsEclipseCopyDropins(type: Copy) { def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar") inputFiles += file("${jalviewDir}/${jalviewjsJ2sPlugin}") - def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" + def destinationDirectory = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" from inputFiles - into outputDir + into destinationDirectory } @@ -3499,13 +3517,13 @@ task jalviewjsSyncAllLibs (type: Sync) { dependsOn jalviewjsTransferUnzipAllLibs def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}") inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}") - def outputDir = "${jalviewDir}/${jalviewjsSiteDir}" + def destinationDirectory = "${jalviewDir}/${jalviewjsSiteDir}" from inputFiles - into outputDir + into destinationDirectory def outputFiles = [] rename { filename -> - outputFiles += "${outputDir}/${filename}" + outputFiles += "${destinationDirectory}/${filename}" null } preserve { @@ -3524,13 +3542,13 @@ task jalviewjsSyncResources (type: Sync) { dependsOn buildResources def inputFiles = fileTree(dir: resourcesBuildDir) - def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}" + def destinationDirectory = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}" from inputFiles - into outputDir + into destinationDirectory def outputFiles = [] rename { filename -> - outputFiles += "${outputDir}/${filename}" + outputFiles += "${destinationDirectory}/${filename}" null } preserve { @@ -3543,13 +3561,13 @@ task jalviewjsSyncResources (type: Sync) { task jalviewjsSyncSiteResources (type: Sync) { def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}") - def outputDir = "${jalviewDir}/${jalviewjsSiteDir}" + def destinationDirectory = "${jalviewDir}/${jalviewjsSiteDir}" from inputFiles - into outputDir + into destinationDirectory def outputFiles = [] rename { filename -> - outputFiles += "${outputDir}/${filename}" + outputFiles += "${destinationDirectory}/${filename}" null } preserve { @@ -3563,13 +3581,13 @@ task jalviewjsSyncSiteResources (type: Sync) { task jalviewjsSyncBuildProperties (type: Sync) { dependsOn createBuildProperties def inputFiles = [file(buildProperties)] - def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}" + def destinationDirectory = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}" from inputFiles - into outputDir + into destinationDirectory def outputFiles = [] rename { filename -> - outputFiles += "${outputDir}/${filename}" + outputFiles += "${destinationDirectory}/${filename}" null } preserve { @@ -3788,7 +3806,7 @@ task jalviewjsBuildAllCores { def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_j2s_subdir}" def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteLibDir}/${jalviewjs_j2s_subdir}" def jsDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_js_subdir}" - def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core" + def destinationDirectory = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core" def prefixFile = "${jsDir}/core/coretop2.js" def suffixFile = "${jsDir}/core/corebottom2.js" @@ -3832,8 +3850,8 @@ task jalviewjsBuildAllCores { } def list = fileTree(dir: j2sDir, includes: filelist) - def jsfile = "${outputDir}/core${name}.js" - def zjsfile = "${outputDir}/core${name}.z.js" + def jsfile = "${destinationDirectory}/core${name}.js" + def zjsfile = "${destinationDirectory}/core${name}.z.js" jalviewjsCoreClasslists += [ 'jsfile': jsfile, @@ -3851,8 +3869,8 @@ task jalviewjsBuildAllCores { // _stevesoft core. add any cores without a classlist here (and the inputs and outputs) def stevesoftClasslistName = "_stevesoft" def stevesoftClasslist = [ - 'jsfile': "${outputDir}/core${stevesoftClasslistName}.js", - 'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js", + 'jsfile': "${destinationDirectory}/core${stevesoftClasslistName}.js", + 'zjsfile': "${destinationDirectory}/core${stevesoftClasslistName}.z.js", 'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"), 'name': stevesoftClasslistName ] @@ -3885,8 +3903,8 @@ task jalviewjsBuildAllCores { ] ) def allClasslist = [ - 'jsfile': "${outputDir}/core${allClasslistName}.js", - 'zjsfile': "${outputDir}/core${allClasslistName}.z.js", + 'jsfile': "${destinationDirectory}/core${allClasslistName}.js", + 'zjsfile': "${destinationDirectory}/core${allClasslistName}.z.js", 'list': allJsFiles, 'name': allClasslistName ] @@ -3939,11 +3957,11 @@ task jalviewjsPublishCoreTemplates { dependsOn jalviewjsBuildAllCores def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}" def inputFile = file(inputFileName) - def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}" + def destinationDirectory = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}" def outputFiles = [] jalviewjsCoreClasslists.each { cl -> - def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html" + def outputFile = "${destinationDirectory}/${jalviewjsJalviewTemplateName}_${cl.name}.html" cl['outputfile'] = outputFile outputFiles += outputFile } @@ -3962,13 +3980,13 @@ task jalviewjsSyncCore (type: Sync) { dependsOn jalviewjsBuildAllCores dependsOn jalviewjsPublishCoreTemplates def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}") - def outputDir = "${jalviewDir}/${jalviewjsSiteDir}" + def destinationDirectory = "${jalviewDir}/${jalviewjsSiteDir}" from inputFiles - into outputDir + into destinationDirectory def outputFiles = [] rename { filename -> - outputFiles += "${outputDir}/${filename}" + outputFiles += "${destinationDirectory}/${filename}" null } preserve { diff --git a/help/markdown/releases/release-2_11_3_0.md b/help/markdown/releases/release-2_11_3_0.md index c732130..dab5393 100644 --- a/help/markdown/releases/release-2_11_3_0.md +++ b/help/markdown/releases/release-2_11_3_0.md @@ -72,13 +72,12 @@ channel: "release" - Installers built with install4j10 - Create separate gradle test task for some tests - Prevent gradle test on macOS continuously grabbing focus -- Allow gradle build to create suffixed DEVELOP-... builds with channel appbase +- Allow gradle build to create suffixed DEVELOP-... builds with channel - Update .jvl generation in build.gradle for jalview branch builds - Jalview bio.tools description maintained under jalview's git repo and bundled with source release - Output stderr and stdout when running tests via gradle - New gradle task providing runtime acceptance test for JalviewJS based on Chromium for Tests (still work in progress) - ## Issues Resolved - Jmol view not always centred on structures when multiple structures are viewed - Unsaved Alignment windows close without prompting to save, individually or at application quit. diff --git a/j11lib/commons-math3-3.6.1.jar b/j11lib/commons-math3-3.6.1.jar new file mode 100644 index 0000000..0ff582c Binary files /dev/null and b/j11lib/commons-math3-3.6.1.jar differ diff --git a/j8lib/commons-math3-3.6.1.jar b/j8lib/commons-math3-3.6.1.jar new file mode 100644 index 0000000..0ff582c Binary files /dev/null and b/j8lib/commons-math3-3.6.1.jar differ diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index 8256e06..293f612 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -98,7 +98,7 @@ action.scale_right = Scale Right action.by_tree_order = By Tree Order action.sort = Sort action.calculate_tree = Calculate Tree... -action.calculate_tree_pca = Calculate Tree or PCA... +action.calculate_tree_pca = Calculate Tree, PCA or PaSiMap... action.help = Help action.by_annotation = By Annotation... action.invert_sequence_selection = Invert Sequence Selection @@ -187,6 +187,7 @@ label.amend = Amend label.undo_command = Undo {0} label.redo_command = Redo {0} label.principal_component_analysis = Principal Component Analysis +label.pasimap = PaSiMap label.average_distance_identity = Average Distance Using % Identity label.neighbour_joining_identity = Neighbour Joining Using % Identity label.choose_calculation = Choose Calculation @@ -319,6 +320,8 @@ label.labels = Labels label.output_values = Output Values... label.output_points = Output points... label.output_transformed_points = Output transformed points +label.output_alignment = Output pairwise alignments +label.pairwise_alignment_for_params = Pairwise alignments for {0} label.input_data = Input Data... label.nucleotide_matrix = Nucleotide matrix label.protein_matrix = Protein matrix @@ -465,6 +468,7 @@ label.selection_output_command = Selection output - {0} label.annotation_for_displayid =

Annotation for {0}

label.pdb_sequence_mapping = PDB - Sequence Mapping label.pca_details = PCA details +label.pasimap_details = PaSiMap details label.redundancy_threshold_selection = Redundancy threshold selection label.user_defined_colours = User defined colours label.jalviewLite_release = JalviewLite - Release {0} @@ -1030,6 +1034,8 @@ label.add_new_sbrs_service = Add a new Simple Bioinformatics Rest Service label.edit_sbrs_entry = Edit Simple Bioinformatics Rest Service entry label.pca_recalculating = Recalculating PCA label.pca_calculating = Calculating PCA +label.pasimap_recalculating = Recalculating PaSiMap +label.pasimap_calculating = Calculating PaSiMap label.select_foreground_colour = Choose foreground colour label.select_colour_for_text = Select Colour for Text label.adjust_foreground_text_colour_threshold = Adjust Foreground Text Colour Threshold @@ -1358,6 +1364,7 @@ label.annotation_description = Annotation Description label.edit_annotation_name_description = Edit Annotation Name/Description label.alignment = alignment label.pca = PCA +label.pasimap = PaSiMap label.create_image_of = Create {0} image of {1} label.click_to_edit = Click to edit, right-click for menu label.backupfiles_confirm_delete = Confirm delete diff --git a/resources/lang/Messages_es.properties b/resources/lang/Messages_es.properties index a0b9292..4936d33 100644 --- a/resources/lang/Messages_es.properties +++ b/resources/lang/Messages_es.properties @@ -94,7 +94,7 @@ action.scale_right = Escala derecha action.by_tree_order = Por orden del árbol action.sort = Ordenar action.calculate_tree = Calcular árbol... -action.calculate_tree_pca = Calcular árbol o ACP... +action.calculate_tree_pca = Calcular árbol, ACP o PaSiMap... action.help = Ayuda action.by_annotation = Por anotación... action.invert_sequence_selection = Invertir selección de secuencias @@ -177,6 +177,7 @@ label.amend = Modificar label.undo_command = Deshacer {0} label.redo_command = Rehacer {0} label.principal_component_analysis = Análisis del Componente Principal +label.pasimap = PaSiMap label.average_distance_identity = Distancia Media Usando % de Identidad label.neighbour_joining_identity = Unir vecinos utilizando % de Identidad label.choose_calculation = Elegir el cálculo @@ -277,6 +278,8 @@ label.labels = Etiquetas label.output_values = Valores de salida... label.output_points = Puntos de salida... label.output_transformed_points = Puntos de salida transformados +label.output_alignment = Alineaciones de pares de salida +label.pairwise_alignment_for_params = Alineaciiones por pares para {0} label.input_data = Datos de entrada... label.nucleotide_matrix = Matriz nucleotídica label.protein_matrix = Matriz proteica @@ -938,6 +941,8 @@ label.add_new_sbrs_service = A label.edit_sbrs_entry = Editar entrada SBRS label.pca_recalculating = Recalculando ACP label.pca_calculating = Calculando ACP +label.pasimap_recalculating = Recalculando PaSiMap +label.pasimap_calculating = Calculando PaSiMap label.select_foreground_colour = Escoger color del primer plano label.select_colour_for_text = Seleccione el color del texto label.adjust_foreground_text_colour_threshold = Ajustar el umbral del color del texto en primer plano @@ -1337,6 +1342,7 @@ label.annotation_description = Descripci label.edit_annotation_name_description = Editar el nombre/descripción de la anotación label.alignment = alineamiento label.pca = ACP +label.pasimap = PaSiMap label.create_image_of = Crear imagen {0} de {1} label.click_to_edit = Haga clic para editar, clic en el botón derecho para ver el menú label.backupfiles_confirm_delete = Confirmar borrar diff --git a/src/jalview/analysis/AlignSeq.java b/src/jalview/analysis/AlignSeq.java index 36d2482..22cffb1 100755 --- a/src/jalview/analysis/AlignSeq.java +++ b/src/jalview/analysis/AlignSeq.java @@ -20,8 +20,6 @@ */ package jalview.analysis; -import java.util.Locale; - import jalview.analysis.scoremodels.PIDModel; import jalview.analysis.scoremodels.ScoreMatrix; import jalview.analysis.scoremodels.ScoreModels; @@ -31,6 +29,7 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.Mapping; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; +import jalview.math.MiscMath; import jalview.util.Comparison; import jalview.util.Format; import jalview.util.MapList; @@ -39,14 +38,15 @@ import jalview.util.MessageManager; import java.awt.Color; import java.awt.Graphics; import java.io.PrintStream; +import java.lang.IllegalArgumentException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.StringTokenizer; +import java.util.Locale; /** - * - * * @author $author$ * @version $Revision$ */ @@ -54,9 +54,13 @@ public class AlignSeq { private static final int MAX_NAME_LENGTH = 30; - private static final int GAP_OPEN_COST = 120; + private static final int DEFAULT_OPENCOST = 120; + + private static final int DEFAULT_EXTENDCOST = 20; + + private int GAP_OPEN_COST=DEFAULT_OPENCOST; - private static final int GAP_EXTEND_COST = 20; + private int GAP_EXTEND_COST=DEFAULT_EXTENDCOST; private static final int GAP_INDEX = -1; @@ -68,6 +72,8 @@ public class AlignSeq float[][] score; + float alignmentScore; + float[][] E; float[][] F; @@ -98,6 +104,10 @@ public class AlignSeq public String astr2 = ""; + public String indelfreeAstr1 = ""; + + public String indelfreeAstr2 = ""; + /** DOCUMENT ME!! */ public int seq1start; @@ -113,6 +123,10 @@ public class AlignSeq public float maxscore; + public float meanScore; //needed for PaSiMap + + public int hypotheticMaxScore; // needed for PaSiMap + int prev = 0; StringBuffer output = new StringBuffer(); @@ -131,6 +145,11 @@ public class AlignSeq * @param type * molecule type, either AlignSeq.PEP or AlignSeq.DNA */ + public AlignSeq(int opencost, int extcost) + { + GAP_OPEN_COST = opencost; + GAP_EXTEND_COST = extcost; + } public AlignSeq(SequenceI s1, SequenceI s2, String type) { seqInit(s1, s1.getSequenceAsString(), s2, s2.getSequenceAsString(), @@ -140,12 +159,12 @@ public class AlignSeq /** * Creates a new AlignSeq object. * - * @param s1 - * DOCUMENT ME! - * @param s2 - * DOCUMENT ME! + * @param s1,string1 + * s1 reference sequence for string1 + * @param s2,string2 + * s2 reference sequence for string2 * @param type - * DOCUMENT ME! + * molecule type, either AlignSeq.PEP or AlignSeq.DNA */ public AlignSeq(SequenceI s1, String string1, SequenceI s2, String string2, String type) @@ -154,6 +173,23 @@ public class AlignSeq string2.toUpperCase(Locale.ROOT), type); } + public AlignSeq(SequenceI s1, SequenceI s2, String type, int opencost, + int extcost) + { + this(s1,s2,type); + GAP_OPEN_COST=opencost; + GAP_EXTEND_COST=extcost; + } + + public AlignSeq(SequenceI s12, String string1, SequenceI s22, + String string2, String type2, int defaultOpencost, + int defaultExtendcost) + { + this(s12,string1,s22,string2,type2); + GAP_OPEN_COST=defaultOpencost; + GAP_EXTEND_COST=defaultExtendcost; + } + /** * DOCUMENT ME! * @@ -165,6 +201,16 @@ public class AlignSeq } /** + * returns the overall score of the alignment + * + * @return + */ + public float getAlignmentScore() + { + return alignmentScore; + } + + /** * DOCUMENT ME! * * @return DOCUMENT ME! @@ -299,6 +345,13 @@ public class AlignSeq public void seqInit(SequenceI s1, String string1, SequenceI s2, String string2, String type) { + seqInit(s1,string1,s2,string2,type,GAP_OPEN_COST,GAP_EXTEND_COST); + } + public void seqInit(SequenceI s1, String string1, SequenceI s2, + String string2, String type, int opening,int extension) + { + GAP_OPEN_COST=opening; + GAP_EXTEND_COST=extension; this.s1 = s1; this.s2 = s2; setDefaultParams(type); @@ -385,8 +438,6 @@ public class AlignSeq int trace; maxscore = score[i][j] / 10f; - seq1end = maxi + 1; - seq2end = maxj + 1; aseq1 = new int[seq1.length + seq2.length]; aseq2 = new int[seq1.length + seq2.length]; @@ -396,6 +447,7 @@ public class AlignSeq count = (seq1.length + seq2.length) - 1; + while (i > 0 && j > 0) { aseq1[count] = seq1[i]; @@ -441,6 +493,137 @@ public class AlignSeq sb2.append(s2str.charAt(j)); } + + /* + * we built the character strings backwards, so now + * reverse them to convert to sequence strings + */ + astr1 = sb1.reverse().toString(); + astr2 = sb2.reverse().toString(); + } + + /** + * DOCUMENT ME! + */ + public void traceAlignmentWithEndGaps() + { + // Find the maximum score along the rhs or bottom row + float max = -Float.MAX_VALUE; + + for (int i = 0; i < seq1.length; i++) + { + if (score[i][seq2.length - 1] > max) + { + max = score[i][seq2.length - 1]; + maxi = i; + maxj = seq2.length - 1; + } + } + + for (int j = 0; j < seq2.length; j++) + { + if (score[seq1.length - 1][j] > max) + { + max = score[seq1.length - 1][j]; + maxi = seq1.length - 1; + maxj = j; + } + } + + int i = maxi; + int j = maxj; + int trace; + maxscore = score[i][j] / 10f; + + //prepare trailing gaps + while ((i < seq1.length - 1) || (j < seq2.length - 1)) + { + i++; + j++; + } + seq1end = i + 1; + seq2end = j + 1; + + aseq1 = new int[seq1.length + seq2.length]; + aseq2 = new int[seq1.length + seq2.length]; + + StringBuilder sb1 = new StringBuilder(aseq1.length); + StringBuilder sb2 = new StringBuilder(aseq2.length); + + count = (seq1.length + seq2.length) - 1; + + //get trailing gaps + while ((i >= seq1.length) || (j >= seq2.length)) + { + if (i >= seq1.length) + { + aseq1[count] = GAP_INDEX; + sb1.append("-"); + aseq2[count] = seq2[j]; + sb2.append(s2str.charAt(j)); + } else if (j >= seq2.length) { + aseq1[count] = seq1[i]; + sb1.append(s1str.charAt(i)); + aseq2[count] = GAP_INDEX; + sb2.append("-"); + } + i--; + j--; + } + + while (i > 0 && j > 0) + { + aseq1[count] = seq1[i]; + sb1.append(s1str.charAt(i)); + aseq2[count] = seq2[j]; + sb2.append(s2str.charAt(j)); + + trace = findTrace(i, j); + + if (trace == 0) + { + i--; + j--; + } + else if (trace == 1) + { + j--; + aseq1[count] = GAP_INDEX; + sb1.replace(sb1.length() - 1, sb1.length(), "-"); + } + else if (trace == -1) + { + i--; + aseq2[count] = GAP_INDEX; + sb2.replace(sb2.length() - 1, sb2.length(), "-"); + } + + count--; + } + + seq1start = i + 1; + seq2start = j + 1; + + aseq1[count] = seq1[i]; + sb1.append(s1str.charAt(i)); + aseq2[count] = seq2[j]; + sb2.append(s2str.charAt(j)); + + //get initial gaps + while (j > 0 || i > 0) + { + if (j > 0) + { + j--; + sb1.append("-"); + sb2.append(s2str.charAt(j)); + } else if (i > 0) { + i--; + sb1.append(s1str.charAt(i)); + sb2.append("-"); + } + } + /* * we built the character strings backwards, so now * reverse them to convert to sequence strings @@ -635,25 +818,26 @@ public class AlignSeq { int n = seq1.length; int m = seq2.length; - + final int GAP_EX_COST=GAP_EXTEND_COST; + final int GAP_OP_COST = GAP_OPEN_COST; // top left hand element score[0][0] = scoreMatrix.getPairwiseScore(s1str.charAt(0), s2str.charAt(0)) * 10; - E[0][0] = -GAP_EXTEND_COST; + E[0][0] = -GAP_EX_COST; F[0][0] = 0; // Calculate the top row first for (int j = 1; j < m; j++) { // What should these values be? 0 maybe - E[0][j] = max(score[0][j - 1] - GAP_OPEN_COST, - E[0][j - 1] - GAP_EXTEND_COST); - F[0][j] = -GAP_EXTEND_COST; + E[0][j] = max(score[0][j - 1] - GAP_OP_COST, + E[0][j - 1] - GAP_EX_COST); + F[0][j] = -GAP_EX_COST; float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(0), s2str.charAt(j)); - score[0][j] = max(pairwiseScore * 10, -GAP_OPEN_COST, - -GAP_EXTEND_COST); + score[0][j] = max(pairwiseScore * 10, -GAP_OP_COST, + -GAP_EX_COST); traceback[0][j] = 1; } @@ -661,9 +845,9 @@ public class AlignSeq // Now do the left hand column for (int i = 1; i < n; i++) { - E[i][0] = -GAP_OPEN_COST; - F[i][0] = max(score[i - 1][0] - GAP_OPEN_COST, - F[i - 1][0] - GAP_EXTEND_COST); + E[i][0] = -GAP_OP_COST; + F[i][0] = max(score[i - 1][0] - GAP_OP_COST, + F[i - 1][0] - GAP_EX_COST); float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(i), s2str.charAt(0)); @@ -676,10 +860,10 @@ public class AlignSeq { for (int j = 1; j < m; j++) { - E[i][j] = max(score[i][j - 1] - GAP_OPEN_COST, - E[i][j - 1] - GAP_EXTEND_COST); - F[i][j] = max(score[i - 1][j] - GAP_OPEN_COST, - F[i - 1][j] - GAP_EXTEND_COST); + E[i][j] = max(score[i][j - 1] - GAP_OP_COST, + E[i][j - 1] - GAP_EX_COST); + F[i][j] = max(score[i - 1][j] - GAP_OP_COST, + F[i - 1][j] - GAP_EX_COST); float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(i), s2str.charAt(j)); @@ -857,7 +1041,13 @@ public class AlignSeq public static AlignSeq doGlobalNWAlignment(SequenceI s1, SequenceI s2, String type) { - AlignSeq as = new AlignSeq(s1, s2, type); + return doGlobalNWAlignment(s1, s2, type, DEFAULT_OPENCOST,DEFAULT_EXTENDCOST); + } + public static AlignSeq doGlobalNWAlignment(SequenceI s1, SequenceI s2, + String type, int opencost,int extcost) + { + + AlignSeq as = new AlignSeq(s1, s2, type,opencost,extcost); as.calcScoreMatrix(); as.traceAlignment(); @@ -1132,4 +1322,181 @@ public class AlignSeq } return redundancy; } + + /** + * calculate the mean score of the alignment + * mean score is equal to the score of an alignmenet of two sequences with randomly shuffled AA sequence composited of the same AA as the two original sequences + * + */ + public void meanScore() + { + int length = indelfreeAstr1.length(); //both have the same length + //create HashMap for counting residues in each sequence + HashMap seq1ResCount = new HashMap(); + HashMap seq2ResCount = new HashMap(); + + // for both sequences (String indelfreeAstr1 or 2) create a key for the residue and add 1 each time its encountered + for (char residue: indelfreeAstr1.toCharArray()) + { + seq1ResCount.putIfAbsent(residue, 0); + seq1ResCount.replace(residue, seq1ResCount.get(residue) + 1); + } + for (char residue: indelfreeAstr2.toCharArray()) + { + seq2ResCount.putIfAbsent(residue, 0); + seq2ResCount.replace(residue, seq2ResCount.get(residue) + 1); + } + + // meanscore = for each residue pair get the number of appearance and add (countA * countB * pairwiseScore(AB)) + // divide the meanscore by the sequence length afterwards + float _meanscore = 0; + for (char resA : seq1ResCount.keySet()) + { + for (char resB : seq2ResCount.keySet()) + { + int countA = seq1ResCount.get(resA); + int countB = seq2ResCount.get(resB); + + float scoreAB = scoreMatrix.getPairwiseScore(resA, resB); + + _meanscore += countA * countB * scoreAB; + } + } + _meanscore /= length; + this.meanScore = _meanscore; + } + + public float getMeanScore() + { + return this.meanScore; + } + + /** + * calculate the hypothetic max score using the self-alignment of the sequences + */ + public void hypotheticMaxScore() + { + int _hmsA = 0; + int _hmsB = 0; + for (char residue: indelfreeAstr1.toCharArray()) + { + _hmsA += scoreMatrix.getPairwiseScore(residue, residue); + } + for (char residue: indelfreeAstr2.toCharArray()) + { + _hmsB += scoreMatrix.getPairwiseScore(residue, residue); + } + this.hypotheticMaxScore = (_hmsA < _hmsB) ? _hmsA : _hmsB; // take the lower self alignment + + } + + public int getHypotheticMaxScore() + { + return this.hypotheticMaxScore; + } + + /** + * create strings based of astr1 and astr2 but without gaps + */ + public void getIndelfreeAstr() + { + int n = astr1.length(); // both have the same length + for (int i = 0; i < n; i++) + { + if (Character.isLetter(astr1.charAt(i)) && Character.isLetter(astr2.charAt(i))) // if both sequences dont have a gap -> add to indelfreeAstr + { + this.indelfreeAstr1 += astr1.charAt(i); + this.indelfreeAstr2 += astr2.charAt(i); + } + } + } + + /** + * calculates the overall score of the alignment + * preprescore = sum of all scores - all penalties + * if preprescore < 1 ~ alignmentScore = Float.NaN > + * alignmentScore = ((preprescore - meanScore) / (hypotheticMaxScore - meanScore)) * coverage + */ + public void scoreAlignment() + { + + getIndelfreeAstr(); + meanScore(); + hypotheticMaxScore(); + // cannot calculate score because denominator would be zero + if (this.hypotheticMaxScore == this.meanScore) + { + this.alignmentScore = Float.NaN; + return; + } + int n = indelfreeAstr1.length(); + + float score = 0; + boolean aGapOpen = false; + boolean bGapOpen = false; + for (int i = 0; i < n; i++) + { + char char1 = indelfreeAstr1.charAt(i); + char char2 = indelfreeAstr2.charAt(i); + boolean aIsLetter = Character.isLetter(char1); + boolean bIsLetter = Character.isLetter(char2); + if (aIsLetter && bIsLetter) // if pair -> get score + { + score += scoreMatrix.getPairwiseScore(char1, char2); + } else if (!aIsLetter && !bIsLetter) { // both are gap -> skip + } else if ((!aIsLetter && aGapOpen) || (!bIsLetter && bGapOpen)) { // one side gapopen -> score - gap_extend + score -= GAP_EXTEND_COST; + } else { // no gap open -> score - gap_open + score -= GAP_OPEN_COST; + } + // adjust GapOpen status in both sequences + aGapOpen = (!aIsLetter) ? true : false; + bGapOpen = (!bIsLetter) ? true : false; + } + + float preprescore = score; // if this score < 1 --> alignment score = Float.NaN + score = (score - this.meanScore) / (this.hypotheticMaxScore - this.meanScore); + int[] _max = MiscMath.findMax(new int[]{astr1.replace("-","").length(), astr2.replace("-","").length()}); // {index of max, max} + float coverage = (float) n / (float) _max[1]; // indelfreeAstr length / longest sequence length + float prescore = score; // only debug + score *= coverage; + + //System.out.println(String.format("prepre-score: %f, pre-score: %f, longlength: %d\nscore: %1.16f, mean: %f, max: %d", preprescore, prescore, _max[1], score, this.meanScore, this.hypotheticMaxScore)); + float minScore = 0f; + this.alignmentScore = (score <= minScore) ? Float.NaN : score; + } + + public void clear() + { + score = null; + alignmentScore = 0f; + E = null; + F = null; + traceback = null; // todo is this actually used? + seq1 = null; + seq2 = null; + s1 = null; + s2 = null; + s1str = null; + s2str = null; + maxi = 0; + maxj = 0; + aseq1 = null; + aseq2 = null; + astr1 = ""; + astr2 = ""; + indelfreeAstr1 = ""; + indelfreeAstr2 = ""; + seq1start = 0; + seq1end = 0; + seq2start = 0; + seq2end = 0; + count = 0; + maxscore = 0f; + meanScore = 0f; //needed for PaSiMap + hypotheticMaxScore = 0; // needed for PaSiMap + prev = 0; + StringBuffer output = new StringBuffer(); + String type = null; // AlignSeq.PEP or AlignSeq.DNA + } } diff --git a/src/jalview/analysis/Connectivity.java b/src/jalview/analysis/Connectivity.java new file mode 100644 index 0000000..a4cbc3a --- /dev/null +++ b/src/jalview/analysis/Connectivity.java @@ -0,0 +1,96 @@ +/* + * 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; + +//import jalview.datamodel.AlignmentView; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.SequenceI; +import jalview.gui.Desktop; +import jalview.gui.JvOptionPane; +import jalview.viewmodel.AlignmentViewport; + +import java.util.Comparator; +import java.util.Hashtable; +import java.util.HashSet; +import java.util.TreeSet; + +/** + * @Author MorellThomas + */ + +public class Connectivity +{ + + /** + * Returns the number of unique connections for each sequence + * only connections with a score of above 0 count + * + * @param av sequences + * @param scores alignment scores + * + * @return connectivity + */ + public static Hashtable getConnectivity(AlignmentViewport av, float[][] scores, byte dim) throws RuntimeException + { + boolean isSelection = av.getSelectionGroup() != null && av.getSelectionGroup().getSize() > 0; + SequenceI[] sequences; + if (isSelection) + { + sequences = (SequenceI[]) av.getAlignmentView(isSelection).getAlignmentAndHiddenColumns(av.getGapCharacter())[0]; + } else { + sequences = av.getAlignment().getSequencesArray(); + } + + Hashtable connectivity = new Hashtable(); + // for each unique connection + for (int i = 0; i < sequences.length; i++) + { + connectivity.putIfAbsent(sequences[i], 0); + for (int j = 0; j < i; j++) + { + connectivity.putIfAbsent(sequences[j], 0); + int iOld = connectivity.get(sequences[i]); + int jOld = connectivity.get(sequences[j]); + // count the connection if its score is not NaN +//System.out.println(String.format("%s - %s : %f", sequences[i].getName(), sequences[j].getName(), scores[i][j])); + if (!Float.isNaN(scores[i][j])) + { + connectivity.put(sequences[i], ++iOld); + connectivity.put(sequences[j], ++jOld); + } + } + } + + // if a sequence has too few connections, abort + connectivity.forEach((sequence, connection) -> + { + System.out.println(String.format("%s: %d", sequence.getName(), connection)); + if (connection < dim) + { + JvOptionPane.showInternalMessageDialog(Desktop.desktop, String.format("Insufficient number of connections for %s (%d, should be %d or more)", sequence.getName(), connection, dim), "Connectivity Error", JvOptionPane.WARNING_MESSAGE); + throw new ConnectivityException(sequence.getName(), connection, dim); + } + } ); + + return connectivity; + } + +} diff --git a/src/jalview/analysis/ConnectivityException.java b/src/jalview/analysis/ConnectivityException.java new file mode 100644 index 0000000..9915f2a --- /dev/null +++ b/src/jalview/analysis/ConnectivityException.java @@ -0,0 +1,57 @@ +/* + * 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; + +public class ConnectivityException extends RuntimeException +{ + private String sequence; + private int connection; + private byte dim; + + public ConnectivityException(String sequence, int connection, byte dim) + { + this("Insufficient number of connections", sequence, connection, dim); + } + + public ConnectivityException(String message, String sequence, int connection, byte dim) + { + super(String.format("%s for %s (%d, should be %d or more)", message, sequence, connection, dim)); + this.sequence = sequence; + this.connection = connection; + this.dim = dim; + } + + public String getSequence() + { + return sequence; + } + + public int getConnection() + { + return connection; + } + + public byte getDim() + { + return dim; + } + +} diff --git a/src/jalview/analysis/Finder.java b/src/jalview/analysis/Finder.java index 21fa2f1..c84c69a 100644 --- a/src/jalview/analysis/Finder.java +++ b/src/jalview/analysis/Finder.java @@ -606,7 +606,8 @@ public class Finder implements FinderI } else { - allFeatures = sf.getAllFeatures(null); + //allFeatures = sf.getAllFeatures(null); + allFeatures = sf.getAllFeatures(); } // so we can check we are advancing when debugging long fpos = 0; diff --git a/src/jalview/analysis/PaSiMap.java b/src/jalview/analysis/PaSiMap.java new file mode 100755 index 0000000..7c1c24a --- /dev/null +++ b/src/jalview/analysis/PaSiMap.java @@ -0,0 +1,316 @@ +/* + * 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; + +import jalview.api.analysis.ScoreModelI; +import jalview.api.analysis.SimilarityParamsI; +import jalview.bin.Console; +import jalview.datamodel.Point; +import jalview.datamodel.SequenceI; +import jalview.datamodel.SequenceGroup; +import jalview.gui.PairwiseAlignPanel; +import jalview.math.Matrix; +import jalview.math.MatrixI; +import jalview.viewmodel.AlignmentViewport; + +import java.io.PrintStream; +import java.util.Hashtable; + +/** + * Performs Principal Component Analysis on given sequences + * @AUTHOR MorellThomas + */ +public class PaSiMap implements Runnable +{ + /* + * inputs + */ + final private AlignmentViewport seqs; + + final private ScoreModelI scoreModel; + + final private byte dim = 8; + + final private int openCost = 100; + + final private int extendCost = 5; + + /* + * outputs + */ + final private PairwiseAlignPanel alignment; + + private MatrixI pairwiseScores; + + private MatrixI eigenMatrix; + + /** + * Constructor given the sequences to compute for, the similarity model to + * use, and a set of parameters for sequence comparison + * + * @param sequences + * @param sm + * @param options + */ + public PaSiMap(AlignmentViewport sequences, ScoreModelI sm, PairwiseAlignPanel pap) + { + this.seqs = sequences; + this.scoreModel = sm; + this.alignment = pap; + } + + /** + * Returns Eigenvalue + * + * @param i + * Index of diagonal within matrix + * + * @return Returns value of diagonal from matrix + */ + public double getEigenvalue(int i) + { + return eigenMatrix.getD()[i]; + } + + /** + * Returns coordinates for each datapoint + * + * @param l + * DOCUMENT ME! + * @param n + * DOCUMENT ME! + * @param mm + * DOCUMENT ME! + * @param factor ~ is 1 + * + * @return DOCUMENT ME! + */ + public Point[] getComponents(int l, int n, int mm, float factor) + { + Point[] out = new Point[getHeight()]; + + for (int i = 0; i < out.length; i++) + { + float x = (float) component(i, l) * factor; + float y = (float) component(i, n) * factor; + float z = (float) component(i, mm) * factor; + out[i] = new Point(x, y, z); + } + + return out; + } + + /** + * DOCUMENT ME! + * + * @param n + * DOCUMENT ME! + * + * @return DOCUMENT ME! + */ + public double[] component(int n) + { + // n = index of eigenvector + double[] out = new double[getWidth()]; + + for (int i = 0; i < out.length; i++) + { + out[i] = component(n, i); + } + + return out; + } + + /** + * DOCUMENT ME! + * + * @param row + * DOCUMENT ME! + * @param n + * DOCUMENT ME! + * + * @return DOCUMENT ME! + */ + double component(int row, int n) + { + return eigenMatrix.getValue(row, n); + } + + /** + * Answers a formatted text report of the PaSiMap calculation results (matrices + * and eigenvalues) suitable for display + * + * @return + */ + public String getDetails() + { + StringBuilder sb = new StringBuilder(1024); + sb.append("PaSiMap calculation using ").append(scoreModel.getName()) + .append(" sequence similarity matrix\n========\n\n"); + PrintStream ps = wrapOutputBuffer(sb); + + /* + * coordinates matrix, with D vector + */ + sb.append(" --- Pairwise correlation coefficients ---\n"); + pairwiseScores.print(ps, "%8.6f "); + ps.println(); + + sb.append(" --- Eigenvalues ---\n"); + eigenMatrix.printD(ps, "%15.4e"); + ps.println(); + + sb.append(" --- Coordinates ---\n"); + eigenMatrix.print(ps, "%8.6f "); + ps.println(); + + return sb.toString(); + } + + /** + * Performs the PaSiMap calculation + * + * creates a new gui/PairwiseAlignPanel with the input sequences (AlignmentViewport) + * uses analysis/AlignSeq to creatue the pairwise alignments and calculate the AlignmentScores (float for each pair) + * gets all float[][] scores from the gui/PairwiseAlignPanel + * checks the connections for each sequence with AlignmentViewport seqs.calculateConnectivity(float[][] scores, int dim) (from analysis/Connectivity) -- throws an Exception if insufficient + * creates a math/MatrixI pairwiseScores of the float[][] scores + * copys the scores and fills the diagonal to create a symmetric matrix using math/Matrix.fillDiagonal() + * performs the analysis/ccAnalysis with the symmetric matrix + * gets the eigenmatrix and the eigenvalues using math/Matrix.tqli() + */ + @Override + public void run() + { + try + { + //alignment = new PairwiseAlignPanel(seqs, true, 100, 5); + alignment.calculate(); + float[][] scores = alignment.getAlignmentScores(); //bigger index first -- eg scores[14][13] + + seqs.calculateConnectivity(scores, dim); + + pairwiseScores = new Matrix(scores); + pairwiseScores.fillDiagonal(); + + eigenMatrix = pairwiseScores.copy(); + + ccAnalysis cc = new ccAnalysis(pairwiseScores, dim); + eigenMatrix = cc.run().mirrorCol(); + + } catch (Exception q) + { + Console.error("Error computing PaSiMap: " + q.getMessage()); + q.printStackTrace(); + } + } + + /** + * Returns a PrintStream that wraps (appends its output to) the given + * StringBuilder + * + * @param sb + * @return + */ + protected PrintStream wrapOutputBuffer(StringBuilder sb) + { + PrintStream ps = new PrintStream(System.out) + { + @Override + public void print(String x) + { + sb.append(x); + } + + @Override + public void println() + { + sb.append("\n"); + } + }; + return ps; + } + + /** + * Answers the N dimensions of the NxM PaSiMap matrix. This is the number of + * sequences involved in the pairwise score calculation. + * + * @return + */ + public int getHeight() + { + // TODO can any of seqs[] be null? + return eigenMatrix.height();// seqs.getSequences().length; + } + + /** + * Answers the M dimensions of the NxM PaSiMap matrix. This is the number of + * sequences involved in the pairwise score calculation. + * + * @return + */ + public int getWidth() + { + // TODO can any of seqs[] be null? + return eigenMatrix.width();// seqs.getSequences().length; + } + + /** + * Answers the sequence pairwise similarity scores which were the first step + * of the PaSiMap calculation + * + * @return + */ + public MatrixI getPairwiseScores() + { + return pairwiseScores; + } + + public void setPairwiseScores(MatrixI m) + { + pairwiseScores = m; + } + + public MatrixI getEigenmatrix() + { + return eigenMatrix; + } + + public void setEigenmatrix(MatrixI m) + { + eigenMatrix = m; + } + + public PairwiseAlignPanel getAlignments() + { + return alignment; + } + + public String getAlignmentOutput() + { + return alignment.getAlignmentOutput(); + } + + public byte getDim() + { + return dim; + } +} diff --git a/src/jalview/analysis/ccAnalysis.java b/src/jalview/analysis/ccAnalysis.java new file mode 100755 index 0000000..4922a54 --- /dev/null +++ b/src/jalview/analysis/ccAnalysis.java @@ -0,0 +1,847 @@ +/* + * 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. + */ + +/* +* Copyright 2018-2022 Kathy Su, Kay Diederichs +* +* This program 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. +* +* This program 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 this program. If not, see . +*/ + +/** +* Ported from https://doi.org/10.1107/S2059798317000699 by +* @AUTHOR MorellThomas +*/ + +package jalview.analysis; + +import jalview.bin.Console; +import jalview.math.MatrixI; +import jalview.math.Matrix; +import jalview.math.MiscMath; + +import java.lang.Math; +import java.lang.System; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Map.Entry; +import java.util.TreeMap; + +import org.apache.commons.math3.linear.Array2DRowRealMatrix; +import org.apache.commons.math3.linear.SingularValueDecomposition; + +/** + * A class to model rectangular matrices of double values and operations on them + */ +public class ccAnalysis +{ + private byte dim = 0; //dimensions + + private MatrixI scoresOld; //input scores + + public ccAnalysis(MatrixI scores, byte dim) + { + // round matrix to .4f to be same as in pasimap + for (int i = 0; i < scores.height(); i++) + { + for (int j = 0; j < scores.width(); j++) + { + if (!Double.isNaN(scores.getValue(i,j))) + { + scores.setValue(i, j, (double) Math.round(scores.getValue(i,j) * (int) 10000) / 10000); + } + } + } + this.scoresOld = scores; + this.dim = dim; + } + + /** + * Initialise a distrust-score for each hypothesis (h) of hSigns + * distrust = conHypNum - proHypNum + * + * @param hSigns ~ hypothesis signs (+/-) for each sequence + * @param scores ~ input score matrix + * + * @return distrustScores + */ + private int[] initialiseDistrusts(byte[] hSigns, MatrixI scores) + { + int[] distrustScores = new int[scores.width()]; + + // loop over symmetric matrix + for (int i = 0; i < scores.width(); i++) + { + byte hASign = hSigns[i]; + int conHypNum = 0; + int proHypNum = 0; + + for (int j = 0; j < scores.width(); j++) + { + double cell = scores.getRow(i)[j]; // value at [i][j] in scores + byte hBSign = hSigns[j]; + if (!Double.isNaN(cell)) + { + byte cellSign = (byte) Math.signum(cell); //check if sign of matrix value fits hyptohesis + if (cellSign == hASign * hBSign) + { + proHypNum++; + } else { + conHypNum++; + } + } + } + distrustScores[i] = conHypNum - proHypNum; //create distrust score for each sequence + } + return distrustScores; + } + + /** + * Optemise hypothesis concerning the sign of the hypothetical value for each hSigns by interpreting the pairwise correlation coefficients as scalar products + * + * @param hSigns ~ hypothesis signs (+/-) + * @param distrustScores + * @param scores ~ input score matrix + * + * @return hSigns + */ + private byte[] optimiseHypothesis(byte[] hSigns, int[] distrustScores, MatrixI scores) + { + // get maximum distrust score + int[] maxes = MiscMath.findMax(distrustScores); + int maxDistrustIndex = maxes[0]; + int maxDistrust = maxes[1]; + + // if hypothesis is not optimal yet + if (maxDistrust > 0) + { + //toggle sign for hI with maximum distrust + hSigns[maxDistrustIndex] *= -1; + // update distrust at same position + distrustScores[maxDistrustIndex] *= -1; + + // also update distrust scores for all hI that were not changed + byte hASign = hSigns[maxDistrustIndex]; + for (int NOTmaxDistrustIndex = 0; NOTmaxDistrustIndex < distrustScores.length; NOTmaxDistrustIndex++) + { + if (NOTmaxDistrustIndex != maxDistrustIndex) + { + byte hBSign = hSigns[NOTmaxDistrustIndex]; + double cell = scores.getValue(maxDistrustIndex, NOTmaxDistrustIndex); + + // distrust only changed if not NaN + if (!Double.isNaN(cell)) + { + byte cellSign = (byte) Math.signum(cell); + // if sign of cell matches hypothesis decrease distrust by 2 because 1 more value supporting and 1 less contradicting + // else increase by 2 + if (cellSign == hASign * hBSign) + { + distrustScores[NOTmaxDistrustIndex] -= 2; + } else { + distrustScores[NOTmaxDistrustIndex] += 2; + } + } + } + } + //further optimisation necessary + return optimiseHypothesis(hSigns, distrustScores, scores); + + } else { + return hSigns; + } + } + + /** + * takes the a symmetric MatrixI as input scores which may contain Double.NaN + * approximate the missing values using hypothesis optimisation + * + * runs analysis + * + * @param scores ~ score matrix + * + * @return + */ + public MatrixI run () throws Exception + { + //initialse eigenMatrix and repMatrix + MatrixI eigenMatrix = scoresOld.copy(); + MatrixI repMatrix = scoresOld.copy(); + try + { + /* + * Calculate correction factor for 2nd and higher eigenvalue(s). + * This correction is NOT needed for the 1st eigenvalue, because the + * unknown (=NaN) values of the matrix are approximated by presuming + * 1-dimensional vectors as the basis of the matrix interpretation as dot + * products. + */ + + System.out.println("Input correlation matrix:"); + eigenMatrix.print(System.out, "%1.4f "); + + int matrixWidth = eigenMatrix.width(); // square matrix, so width == height + int matrixElementsTotal = (int) Math.pow(matrixWidth, 2); //total number of elemts + + float correctionFactor = (float) (matrixElementsTotal - eigenMatrix.countNaN()) / (float) matrixElementsTotal; + + /* + * Calculate hypothetical value (1-dimensional vector) h_i for each + * dataset by interpreting the given correlation coefficients as scalar + * products. + */ + + /* + * Memory for current hypothesis concerning sign of each h_i. + * List of signs for all h_i in the encoding: + * * 1: positive + * * 0: zero + * * -1: negative + * Initial hypothesis: all signs are positive. + */ + byte[] hSigns = new byte[matrixWidth]; + Arrays.fill(hSigns, (byte) 1); + + //Estimate signs for each h_i by refining hypothesis on signs. + hSigns = optimiseHypothesis(hSigns, initialiseDistrusts(hSigns, eigenMatrix), eigenMatrix); + + + //Estimate absolute values for each h_i by determining sqrt of mean of + //non-NaN absolute values for every row. + double[] hAbs = MiscMath.sqrt(eigenMatrix.absolute().meanRow()); + + //Combine estimated signs with absolute values in obtain total value for + //each h_i. + double[] hValues = MiscMath.elementwiseMultiply(hSigns, hAbs); + + /*Complement symmetric matrix by using the scalar products of estimated + *values of h_i to replace NaN-cells. + *Matrix positions that have estimated values + *(only for diagonal and upper off-diagonal values, due to the symmetry + *the positions of the lower-diagonal values can be inferred). + List of tuples (row_idx, column_idx).*/ + + ArrayList estimatedPositions = new ArrayList(); + + // for off-diagonal cells + for (int rowIndex = 0; rowIndex < matrixWidth - 1; rowIndex++) + { + for (int columnIndex = rowIndex + 1; columnIndex < matrixWidth; columnIndex++) + { + double cell = eigenMatrix.getValue(rowIndex, columnIndex); + if (Double.isNaN(cell)) + { + //calculate scalar product as new cell value + cell = hValues[rowIndex] * hValues[columnIndex]; + //fill in new value in cell and symmetric partner + eigenMatrix.setValue(rowIndex, columnIndex, cell); + eigenMatrix.setValue(columnIndex, rowIndex, cell); + //save positions of estimated values + estimatedPositions.add(new int[]{rowIndex, columnIndex}); + } + } + } + + // for diagonal cells + for (int diagonalIndex = 0; diagonalIndex < matrixWidth; diagonalIndex++) + { + double cell = Math.pow(hValues[diagonalIndex], 2); + eigenMatrix.setValue(diagonalIndex, diagonalIndex, cell); + estimatedPositions.add(new int[]{diagonalIndex, diagonalIndex}); + } + + /*Refine total values of each h_i: + *Initialise h_values of the hypothetical non-existant previous iteration + *with the correct format but with impossible values. + Needed for exit condition of otherwise endless loop.*/ + System.out.print("initial values: [ "); + for (double h : hValues) + { + System.out.print(String.format("%1.4f, ", h)); + } + System.out.println(" ]"); + + + double[] hValuesOld = new double[matrixWidth]; + + int iterationCount = 0; + + // repeat unitl values of h do not significantly change anymore + while (true) + { + for (int hIndex = 0; hIndex < matrixWidth; hIndex++) + { + double newH = Arrays.stream(MiscMath.elementwiseMultiply(hValues, eigenMatrix.getRow(hIndex))).sum() / Arrays.stream(MiscMath.elementwiseMultiply(hValues, hValues)).sum(); + hValues[hIndex] = newH; + } + + System.out.print(String.format("iteration %d: [ ", iterationCount)); + for (double h : hValues) + { + System.out.print(String.format("%1.4f, ", h)); + } + System.out.println(" ]"); + + //update values of estimated positions + for (int[] pair : estimatedPositions) // pair ~ row, col + { + double newVal = hValues[pair[0]] * hValues[pair[1]]; + eigenMatrix.setValue(pair[0], pair[1], newVal); + eigenMatrix.setValue(pair[1], pair[0], newVal); + } + + iterationCount++; + + //exit loop as soon as new values are similar to the last iteration + if (MiscMath.allClose(hValues, hValuesOld, 0d, 1e-05d, false)) + { + break; + } + + //save hValues for comparison in the next iteration + System.arraycopy(hValues, 0, hValuesOld, 0, hValues.length); + } + + //----------------------------- + //Use complemented symmetric matrix to calculate final representative + //vectors. + + //Eigendecomposition. + eigenMatrix.tred(); + eigenMatrix.tqli(); + + System.out.println("eigenmatrix"); + eigenMatrix.print(System.out, "%8.2f"); + System.out.println(); + System.out.println("uncorrected eigenvalues"); + eigenMatrix.printD(System.out, "%2.4f "); + System.out.println(); + + double[] eigenVals = eigenMatrix.getD(); + + TreeMap eigenPairs = new TreeMap<>(Comparator.reverseOrder()); + for (int i = 0; i < eigenVals.length; i++) + { + eigenPairs.put(eigenVals[i], i); + } + + // matrix of representative eigenvectors (each row is a vector) + double[][] _repMatrix = new double[eigenVals.length][dim]; + double[][] _oldMatrix = new double[eigenVals.length][dim]; + double[] correctedEigenValues = new double[dim]; + + int l = 0; + for (Entry pair : eigenPairs.entrySet()) + { + double eigenValue = pair.getKey(); + int column = pair.getValue(); + double[] eigenVector = eigenMatrix.getColumn(column); + //for 2nd and higher eigenvalues + if (l >= 1) + { + eigenValue /= correctionFactor; + } + correctedEigenValues[l] = eigenValue; + for (int j = 0; j < eigenVector.length; j++) + { + _repMatrix[j][l] = (eigenValue < 0) ? 0.0 : - Math.sqrt(eigenValue) * eigenVector[j]; + double tmpOldScore = scoresOld.getColumn(column)[j]; + _oldMatrix[j][dim - l - 1] = (Double.isNaN(tmpOldScore)) ? 0.0 : tmpOldScore; + } + l++; + if (l >= dim) + { + break; + } + } + + System.out.println("correctedEigenValues"); + MiscMath.print(correctedEigenValues, "%2.4f "); + + repMatrix = new Matrix(_repMatrix); + repMatrix.setD(correctedEigenValues); + MatrixI oldMatrix = new Matrix(_oldMatrix); + + MatrixI dotMatrix = repMatrix.postMultiply(repMatrix.transpose()); + + double rmsd = scoresOld.rmsd(dotMatrix); + + System.out.println("iteration, rmsd, maxDiff, rmsdDiff"); + System.out.println(String.format("0, %8.5f, -, -", rmsd)); + // Refine representative vectors by minimising sum-of-squared deviates between dotMatrix and original score matrix + for (int iteration = 1; iteration < 21; iteration++) // arbitrarily set to 20 + { + MatrixI repMatrixOLD = repMatrix.copy(); + MatrixI dotMatrixOLD = dotMatrix.copy(); + + // for all rows/hA in the original matrix + for (int hAIndex = 0; hAIndex < oldMatrix.height(); hAIndex++) + { + double[] row = oldMatrix.getRow(hAIndex); + double[] hA = repMatrix.getRow(hAIndex); + hAIndex = hAIndex; + //find least-squares-solution fo rdifferences between original scores and representative vectors + double[] hAlsm = leastSquaresOptimisation(repMatrix, scoresOld, hAIndex); + // update repMatrix with new hAlsm + for (int j = 0; j < repMatrix.width(); j++) + { + repMatrix.setValue(hAIndex, j, hAlsm[j]); + } + } + + // dot product of representative vecotrs yields a matrix with values approximating the correlation matrix + dotMatrix = repMatrix.postMultiply(repMatrix.transpose()); + // calculate rmsd between approximation and correlation matrix + rmsd = scoresOld.rmsd(dotMatrix); + + // calculate maximum change of representative vectors of current iteration + MatrixI diff = repMatrix.subtract(repMatrixOLD).absolute(); + double maxDiff = 0.0; + for (int i = 0; i < diff.height(); i++) + { + for (int j = 0; j < diff.width(); j++) + { + maxDiff = (diff.getValue(i, j) > maxDiff) ? diff.getValue(i, j) : maxDiff; + } + } + + // calculate rmsd between current and previous estimation + double rmsdDiff = dotMatrix.rmsd(dotMatrixOLD); + + System.out.println(String.format("%d, %8.5f, %8.5f, %8.5f", iteration, rmsd, maxDiff, rmsdDiff)); + + if (!(Math.abs(maxDiff) > 1e-06)) + { + repMatrix = repMatrixOLD.copy(); + break; + } + } + + + } catch (Exception q) + { + Console.error("Error computing cc_analysis: " + q.getMessage()); + q.printStackTrace(); + } + System.out.println("final coordinates:"); + repMatrix.print(System.out, "%1.8f "); + return repMatrix; + } + + /** + * Create equations system using information on originally known + * pairwise correlation coefficients (parsed from infile) and the + * representative result vectors + * + * Each equation has the format: + * hA * hA - pairwiseCC = 0 + * with: + * hA: unknown variable + * hB: known representative vector + * pairwiseCC: known pairwise correlation coefficien + * + * The resulting equations system is overdetermined, if there are more + * equations than unknown elements + * + * @param x ~ unknown n-dimensional column-vector + * (needed for generating equations system, NOT to be specified by user). + * @param hAIndex ~ index of currently optimised representative result vector. + * @param h ~ matrix with row-wise listing of representative result vectors. + * @param originalRow ~ matrix-row of originally parsed pairwise correlation coefficients. + * + * @return + */ + private double[] originalToEquasionSystem(double[] hA, MatrixI repMatrix, MatrixI scoresOld, int hAIndex) + { + double[] originalRow = scoresOld.getRow(hAIndex); + int nans = MiscMath.countNaN(originalRow); + double[] result = new double[originalRow.length - nans]; + + //for all pairwiseCC in originalRow + int resultIndex = 0; + for (int hBIndex = 0; hBIndex < originalRow.length; hBIndex++) + { + double pairwiseCC = originalRow[hBIndex]; + // if not NaN -> create new equation and add it to the system + if (!Double.isNaN(pairwiseCC)) + { + double[] hB = repMatrix.getRow(hBIndex); + result[resultIndex++] = MiscMath.sum(MiscMath.elementwiseMultiply(hA, hB)) - pairwiseCC; + } else { + } + } + return result; + } + + /** + * returns the jacobian matrix + * @param repMatrix ~ matrix of representative vectors + * @param hAIndex ~ current row index + * + * @return + */ + private MatrixI approximateDerivative(MatrixI repMatrix, MatrixI scoresOld, int hAIndex) + { + //hA = x0 + double[] hA = repMatrix.getRow(hAIndex); + double[] f0 = originalToEquasionSystem(hA, repMatrix, scoresOld, hAIndex); + double[] signX0 = new double[hA.length]; + double[] xAbs = new double[hA.length]; + for (int i = 0; i < hA.length; i++) + { + signX0[i] = (hA[i] >= 0) ? 1 : -1; + xAbs[i] = (Math.abs(hA[i]) >= 1.0) ? Math.abs(hA[i]) : 1.0; + } + double rstep = Math.pow(Math.ulp(1.0), 0.5); + + double[] h = new double [hA.length]; + for (int i = 0; i < hA.length; i++) + { + h[i] = rstep * signX0[i] * xAbs[i]; + } + + int m = f0.length; + int n = hA.length; + double[][] jTransposed = new double[n][m]; + for (int i = 0; i < h.length; i++) + { + double[] x = new double[h.length]; + System.arraycopy(hA, 0, x, 0, h.length); + x[i] += h[i]; + double dx = x[i] - hA[i]; + double[] df = originalToEquasionSystem(x, repMatrix, scoresOld, hAIndex); + for (int j = 0; j < df.length; j++) + { + df[j] -= f0[j]; + jTransposed[i][j] = df[j] / dx; + } + } + MatrixI J = new Matrix(jTransposed).transpose(); + return J; + } + + /** + * norm of regularized (by alpha) least-squares solution minus Delta + * @param alpha + * @param suf + * @param s + * @param Delta + * + * @return + */ + private double[] phiAndDerivative(double alpha, double[] suf, double[] s, double Delta) + { + double[] denom = MiscMath.elementwiseAdd(MiscMath.elementwiseMultiply(s, s), alpha); + double pNorm = MiscMath.norm(MiscMath.elementwiseDivide(suf, denom)); + double phi = pNorm - Delta; + // - sum ( suf**2 / denom**3) / pNorm + double phiPrime = - MiscMath.sum(MiscMath.elementwiseDivide(MiscMath.elementwiseMultiply(suf, suf), MiscMath.elementwiseMultiply(MiscMath.elementwiseMultiply(denom, denom), denom))) / pNorm; + return new double[]{phi, phiPrime}; + } + + /** + * class holding the result of solveLsqTrustRegion + */ + private class TrustRegion + { + private double[] step; + private double alpha; + private int iteration; + + public TrustRegion(double[] step, double alpha, int iteration) + { + this.step = step; + this.alpha = alpha; + this.iteration = iteration; + } + + public double[] getStep() + { + return this.step; + } + + public double getAlpha() + { + return this.alpha; + } + + public int getIteration() + { + return this.iteration; + } + } + + /** + * solve a trust-region problem arising in least-squares optimisation + * @param n ~ number of variables + * @param m ~ number of residuals + * @param uf + * @param s ~ singular values of J + * @param V ~ transpose of VT + * @param Delta ~ radius of a trust region + * @param alpha ~ initial guess for alpha + * + * @return + */ + private TrustRegion solveLsqTrustRegion(int n, int m, double[] uf, double[] s, MatrixI V, double Delta, double alpha) + { + double[] suf = MiscMath.elementwiseMultiply(s, uf); + + //check if J has full rank and tr Gauss-Newton step + boolean fullRank = false; + if (m >= n) + { + double threshold = s[0] * Math.ulp(1.0) * m; + fullRank = s[s.length - 1] > threshold; + } + if (fullRank) + { + double[] p = MiscMath.elementwiseMultiply(V.sumProduct(MiscMath.elementwiseDivide(uf, s)), -1); + if (MiscMath.norm(p) <= Delta) + { + TrustRegion result = new TrustRegion(p, 0.0, 0); + return result; + } + } + + double alphaUpper = MiscMath.norm(suf) / Delta; + double alphaLower = 0.0; + if (fullRank) + { + double[] phiAndPrime = phiAndDerivative(0.0, suf, s, Delta); + alphaLower = - phiAndPrime[0] / phiAndPrime[1]; + } + + alpha = (!fullRank && alpha == 0.0) ? alpha = Math.max(0.001 * alphaUpper, Math.pow(alphaLower * alphaUpper, 0.5)) : alpha; + + int iteration = 0; + while (iteration < 10) // 10 is default max_iter + { + alpha = (alpha < alphaLower || alpha > alphaUpper) ? alpha = Math.max(0.001 * alphaUpper, Math.pow(alphaLower * alphaUpper, 0.5)) : alpha; + double[] phiAndPrime = phiAndDerivative(alpha, suf, s, Delta); + double phi = phiAndPrime[0]; + double phiPrime = phiAndPrime[1]; + + alphaUpper = (phi < 0) ? alpha : alphaUpper; + double ratio = phi / phiPrime; + alphaLower = Math.max(alphaLower, alpha - ratio); + alpha -= (phi + Delta) * ratio / Delta; + + if (Math.abs(phi) < 0.01 * Delta) // default rtol set to 0.01 + { + break; + } + iteration++; + } + + // p = - V.dot( suf / (s**2 + alpha)) + double[] tmp = MiscMath.elementwiseDivide(suf, MiscMath.elementwiseAdd(MiscMath.elementwiseMultiply(s, s), alpha)); + double[] p = MiscMath.elementwiseMultiply(V.sumProduct(tmp), -1); + + // Make the norm of p equal to Delta, p is changed only slightly during this. + // It is done to prevent p lie outside of the trust region + p = MiscMath.elementwiseMultiply(p, Delta / MiscMath.norm(p)); + + TrustRegion result = new TrustRegion(p, alpha, iteration + 1); + return result; + } + + /** + * compute values of a quadratic function arising in least squares + * function: 0.5 * s.T * (J.T * J + diag) * s + g.T * s + * + * @param J ~ jacobian matrix + * @param g ~ gradient + * @param s ~ steps and rows + * + * @return + */ + private double evaluateQuadratic(MatrixI J, double[] g, double[] s) + { + + double[] Js = J.sumProduct(s); + double q = MiscMath.dot(Js, Js); + double l = MiscMath.dot(s, g); + + return 0.5 * q + l; + } + + /** + * update the radius of a trust region based on the cost reduction + * + * @param Delta + * @param actualReduction + * @param predictedReduction + * @param stepNorm + * @param boundHit + * + * @return + */ + private double[] updateTrustRegionRadius(double Delta, double actualReduction, double predictedReduction, double stepNorm, boolean boundHit) + { + double ratio = 0; + if (predictedReduction > 0) + { + ratio = actualReduction / predictedReduction; + } else if (predictedReduction == 0 && actualReduction == 0) { + ratio = 1; + } else { + ratio = 0; + } + + if (ratio < 0.25) + { + Delta = 0.25 * stepNorm; + } else if (ratio > 0.75 && boundHit) { + Delta *= 2.0; + } + + return new double[]{Delta, ratio}; + } + + /** + * trust region reflective algorithm + * @param repMatrix ~ Matrix containing representative vectors + * @param scoresOld ~ Matrix containing initial observations + * @param index ~ current row index + * @param J ~ jacobian matrix + * + * @return + */ + private double[] trf(MatrixI repMatrix, MatrixI scoresOld, int index, MatrixI J) + { + //hA = x0 + double[] hA = repMatrix.getRow(index); + double[] f0 = originalToEquasionSystem(hA, repMatrix, scoresOld, index); + int nfev = 1; + int m = J.height(); + int n = J.width(); + double cost = 0.5 * MiscMath.dot(f0, f0); + double[] g = J.transpose().sumProduct(f0); + double Delta = MiscMath.norm(hA); + int maxNfev = hA.length * 100; + double alpha = 0.0; // "Levenberg-Marquardt" parameter + + double gNorm = 0; + boolean terminationStatus = false; + int iteration = 0; + + while (true) + { + gNorm = MiscMath.norm(g); + if (terminationStatus || nfev == maxNfev) + { + break; + } + SingularValueDecomposition svd = new SingularValueDecomposition(new Array2DRowRealMatrix(J.asArray())); + MatrixI U = new Matrix(svd.getU().getData()); + double[] s = svd.getSingularValues(); + MatrixI V = new Matrix(svd.getV().getData()).transpose(); + double[] uf = U.transpose().sumProduct(f0); + + double actualReduction = -1; + double[] xNew = new double[hA.length]; + double[] fNew = new double[f0.length]; + double costNew = 0; + double stepHnorm = 0; + + while (actualReduction <= 0 && nfev < maxNfev) + { + TrustRegion trustRegion = solveLsqTrustRegion(n, m, uf, s, V, Delta, alpha); + double[] stepH = trustRegion.getStep(); + alpha = trustRegion.getAlpha(); + int nIterations = trustRegion.getIteration(); + double predictedReduction = - (evaluateQuadratic(J, g, stepH)); + + xNew = MiscMath.elementwiseAdd(hA, stepH); + fNew = originalToEquasionSystem(xNew, repMatrix, scoresOld, index); + nfev++; + + stepHnorm = MiscMath.norm(stepH); + + if (MiscMath.countNaN(fNew) > 0) + { + Delta = 0.25 * stepHnorm; + continue; + } + + // usual trust-region step quality estimation + costNew = 0.5 * MiscMath.dot(fNew, fNew); + actualReduction = cost - costNew; + + double[] updatedTrustRegion = updateTrustRegionRadius(Delta, actualReduction, predictedReduction, stepHnorm, stepHnorm > (0.95 * Delta)); + double DeltaNew = updatedTrustRegion[0]; + double ratio = updatedTrustRegion[1]; + + // default ftol and xtol = 1e-8 + boolean ftolSatisfied = actualReduction < (1e-8 * cost) && ratio > 0.25; + boolean xtolSatisfied = stepHnorm < (1e-8 * (1e-8 + MiscMath.norm(hA))); + terminationStatus = ftolSatisfied || xtolSatisfied; + if (terminationStatus) + { + break; + } + + alpha *= Delta / DeltaNew; + Delta = DeltaNew; + + } + if (actualReduction > 0) + { + hA = xNew; + f0 = fNew; + cost = costNew; + + J = approximateDerivative(repMatrix, scoresOld, index); + + g = J.transpose().sumProduct(f0); + } else { + stepHnorm = 0; + actualReduction = 0; + } + iteration++; + } + + return hA; + } + + /** + * performs the least squares optimisation + * adapted from https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.least_squares.html#scipy.optimize.least_squares + * + * @param repMatrix ~ Matrix containing representative vectors + * @param scoresOld ~ Matrix containing initial observations + * @param index ~ current row index + * + * @return + */ + private double[] leastSquaresOptimisation(MatrixI repMatrix, MatrixI scoresOld, int index) + { + MatrixI J = approximateDerivative(repMatrix, scoresOld, index); + double[] result = trf(repMatrix, scoresOld, index, J); + return result; + } + +} diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index 1aca4d4..7116cf3 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -63,7 +63,6 @@ import java.util.Vector; import javax.swing.AbstractButton; import javax.swing.ButtonGroup; -import javax.swing.ButtonModel; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; import javax.swing.JEditorPane; @@ -73,6 +72,7 @@ import javax.swing.JLayeredPane; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPanel; +import javax.swing.JProgressBar; import javax.swing.JRadioButtonMenuItem; import javax.swing.JScrollPane; import javax.swing.SwingUtilities; @@ -997,6 +997,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } @Override + public JProgressBar getProgressBar(long id) + { + if (progressBar != null) + return progressBar.getProgressBar(id); + return null; + } + + @Override public void registerHandler(final long id, final IProgressIndicatorHandler handler) { diff --git a/src/jalview/gui/CalculationChooser.java b/src/jalview/gui/CalculationChooser.java index 4e22a74..80102cb 100644 --- a/src/jalview/gui/CalculationChooser.java +++ b/src/jalview/gui/CalculationChooser.java @@ -20,6 +20,15 @@ */ package jalview.gui; +import jalview.analysis.TreeBuilder; +import jalview.analysis.scoremodels.ScoreModels; +import jalview.analysis.scoremodels.SimilarityParams; +import jalview.api.analysis.ScoreModelI; +import jalview.api.analysis.SimilarityParamsI; +import jalview.bin.Cache; +import jalview.datamodel.SequenceGroup; +import jalview.util.MessageManager; +import jalview.viewmodel.AlignmentViewport; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; @@ -97,10 +106,14 @@ public class CalculationChooser extends JPanel } + private static final int MIN_PASIMAP_SELECTION = 8; + AlignFrame af; JRadioButton pca; + JRadioButton pasimap; + JRadioButton neighbourJoining; JRadioButton averageDistance; @@ -130,6 +143,8 @@ public class CalculationChooser extends JPanel */ private PCAPanel pcaPanel; + private PaSiMapPanel pasimapPanel; + /** * Constructor * @@ -179,6 +194,10 @@ public class CalculationChooser extends JPanel MessageManager.getString("label.principal_component_analysis")); pca.setOpaque(false); + pasimap = new JRadioButton( // create the JRadioButton for pasimap with label.pasimap as its text + MessageManager.getString("label.pasimap")); + pasimap.setOpaque(false); + neighbourJoining = new JRadioButton( MessageManager.getString("label.tree_calc_nj")); neighbourJoining.setSelected(true); @@ -208,6 +227,14 @@ public class CalculationChooser extends JPanel pcaBorderless.add(pca, FlowLayout.LEFT); calcChoicePanel.add(pcaBorderless, FlowLayout.LEFT); + // create pasimap panel + JPanel pasimapBorderless = new JPanel(new FlowLayout(FlowLayout.LEFT)); // create new JPanel (button) for pasimap + pasimapBorderless.setBorder( + BorderFactory.createEmptyBorder(2, b.left, 2, b.right)); // set border (margin) for button (same as treePanel and pca) + pasimapBorderless.setOpaque(false); // false -> stops every pixel inside border from being painted + pasimapBorderless.add(pasimap, FlowLayout.LEFT); // add pasimap button to the JPanel + calcChoicePanel.add(pasimapBorderless, FlowLayout.LEFT); // add button with border and everything to the overall ChoicePanel + treePanel.add(neighbourJoining); treePanel.add(averageDistance); @@ -215,6 +242,7 @@ public class CalculationChooser extends JPanel ButtonGroup calcTypes = new ButtonGroup(); calcTypes.add(pca); + calcTypes.add(pasimap); calcTypes.add(neighbourJoining); calcTypes.add(averageDistance); @@ -227,6 +255,7 @@ public class CalculationChooser extends JPanel } }; pca.addActionListener(calcChanged); + pasimap.addActionListener(calcChanged); // add the calcChanged ActionListener to pasimap --> <++> idk neighbourJoining.addActionListener(calcChanged); averageDistance.addActionListener(calcChanged); @@ -357,12 +386,13 @@ public class CalculationChooser extends JPanel * return value of true means enabled and selected */ boolean checkPca = checkEnabled(pca, size, MIN_PCA_SELECTION); + boolean checkPasimap = checkEnabled(pasimap, size, MIN_PASIMAP_SELECTION); // check if pasimap is enabled and min_size is fulfilled boolean checkNeighbourJoining = checkEnabled(neighbourJoining, size, MIN_TREE_SELECTION); boolean checkAverageDistance = checkEnabled(averageDistance, size, MIN_TREE_SELECTION); - if (checkPca || checkNeighbourJoining || checkAverageDistance) + if (checkPca || checkPasimap || checkNeighbourJoining || checkAverageDistance) { calculate.setToolTipText(null); calculate.setEnabled(true); @@ -606,6 +636,7 @@ public class CalculationChooser extends JPanel protected void calculate_actionPerformed() { boolean doPCA = pca.isSelected(); + boolean doPaSiMap = pasimap.isSelected(); String modelName = modelNames.getSelectedItem().toString(); String ssSource = ""; Object selectedItem = ssSourceDropdown.getSelectedItem(); @@ -617,16 +648,21 @@ public class CalculationChooser extends JPanel { params.setSecondaryStructureSource(ssSource); } - if (doPCA) + + if (doPCA && !doPaSiMap) { openPcaPanel(modelName, params); } + else if (doPaSiMap && !doPCA) + { + openPasimapPanel(modelName, params); + } else { openTreePanel(modelName, params); } - // closeFrame(); + closeFrame(); } /** @@ -698,6 +734,43 @@ public class CalculationChooser extends JPanel } /** + * Open a new PaSiMap panel on the desktop + * + * @param modelName + * @param params + */ + protected void openPasimapPanel(String modelName, SimilarityParamsI params) + { + AlignViewport viewport = af.getViewport(); + + /* + * gui validation shouldn't allow insufficient sequences here, but leave + * this check in in case this method gets exposed programmatically in future + */ + if (((viewport.getSelectionGroup() != null) + && (viewport.getSelectionGroup().getSize() < MIN_PASIMAP_SELECTION) + && (viewport.getSelectionGroup().getSize() > 0)) + || (viewport.getAlignment().getHeight() < MIN_PASIMAP_SELECTION)) + { + JvOptionPane.showInternalMessageDialog(this, + MessageManager.formatMessage( + "label.you_need_at_least_n_sequences", + MIN_PASIMAP_SELECTION), + MessageManager + .getString("label.sequence_selection_insufficient"), + JvOptionPane.WARNING_MESSAGE); + return; + } + + /* + * construct the panel and kick off its calculation thread + */ + pasimapPanel = new PaSiMapPanel(af.alignPanel, modelName, params); + new Thread(pasimapPanel).start(); + + } + + /** * */ protected void closeFrame() diff --git a/src/jalview/gui/CutAndPasteTransfer.java b/src/jalview/gui/CutAndPasteTransfer.java index bc3c0d2..90a3822 100644 --- a/src/jalview/gui/CutAndPasteTransfer.java +++ b/src/jalview/gui/CutAndPasteTransfer.java @@ -20,6 +20,7 @@ */ package jalview.gui; +import java.awt.Font; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.DataFlavor; @@ -91,6 +92,15 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer } /** + * set font size of the textarea + * @param size + */ + public void setFont(Font font) + { + textarea.setFont(font); + } + + /** * DOCUMENT ME! */ public void setForInput(AlignmentViewPanel viewpanel) @@ -206,6 +216,16 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer { } } + + /** + * show menu for changing the font + * @param e + */ + @Override + public void fontSizeMenu_actionPerformed(ActionEvent e) + { + new FontChooser(this); + } /** * DOCUMENT ME! diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java index bbd4dae..0de6b17 100644 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@ -2809,6 +2809,23 @@ public class Desktop extends jalview.jbgui.GDesktop } } + @Override + public JProgressBar getProgressBar(long id) + { + if (progressBars == null) + return null; + + if (progressBars.get(Long.valueOf(id)) == null) + return null; + + for (Component c : progressBars.get(Long.valueOf(id)).getComponents()) + { + if (c.getClass() == JProgressBar.class) + return (JProgressBar) c; + } + return null; + } + /* * (non-Javadoc) * diff --git a/src/jalview/gui/FontChooser.java b/src/jalview/gui/FontChooser.java index f532706..853d4aa 100755 --- a/src/jalview/gui/FontChooser.java +++ b/src/jalview/gui/FontChooser.java @@ -43,6 +43,8 @@ public class FontChooser extends GFontChooser TreePanel tp; + CutAndPasteTransfer cap; + /* * The font on opening the dialog (to be restored on Cancel) */ @@ -109,30 +111,44 @@ public class FontChooser extends GFontChooser init(); } + /** + * Creates a new FontChooser for a CutAndPasteTransfer + * @param cap + */ + public FontChooser(CutAndPasteTransfer cap) + { + oldFont = new Font("Monospaced", Font.PLAIN, 12); + this.cap = cap; + init(); + } + void init() { frame = new JInternalFrame(); frame.setFrameIcon(null); frame.setContentPane(this); - smoothFont.setSelected(ap.av.antiAlias); - - /* - * Enable 'scale protein as cDNA' in a SplitFrame view. The selection is - * stored in the ViewStyle of both dna and protein Viewport. Also enable - * checkbox for copy font changes to other half of split frame. - */ - boolean inSplitFrame = ap.av.getCodingComplement() != null; - if (inSplitFrame) + if (!isCapFont()) { - oldComplementFont = ((AlignViewport) ap.av.getCodingComplement()) - .getFont(); - oldComplementSmooth = ((AlignViewport) ap.av - .getCodingComplement()).antiAlias; - scaleAsCdna.setVisible(true); - scaleAsCdna.setSelected(ap.av.isScaleProteinAsCdna()); - fontAsCdna.setVisible(true); - fontAsCdna.setSelected(ap.av.isProteinFontAsCdna()); + smoothFont.setSelected(ap.av.antiAlias); + + /* + * Enable 'scale protein as cDNA' in a SplitFrame view. The selection is + * stored in the ViewStyle of both dna and protein Viewport. Also enable + * checkbox for copy font changes to other half of split frame. + */ + boolean inSplitFrame = ap.av.getCodingComplement() != null; + if (inSplitFrame) + { + oldComplementFont = ((AlignViewport) ap.av.getCodingComplement()) + .getFont(); + oldComplementSmooth = ((AlignViewport) ap.av + .getCodingComplement()).antiAlias; + scaleAsCdna.setVisible(true); + scaleAsCdna.setSelected(ap.av.isScaleProteinAsCdna()); + fontAsCdna.setVisible(true); + fontAsCdna.setSelected(ap.av.isProteinFontAsCdna()); + } } if (isTreeFont()) @@ -269,6 +285,10 @@ public class FontChooser extends GFontChooser return tp != null; } + private boolean isCapFont() + { + return cap != null; + } /** * DOCUMENT ME! */ @@ -350,6 +370,10 @@ public class FontChooser extends GFontChooser splitFrame.repaint(); } } + else if (isCapFont()) + { + cap.setFont(newFont); + } monospaced.setSelected(mw == iw); diff --git a/src/jalview/gui/IProgressIndicator.java b/src/jalview/gui/IProgressIndicator.java index 35bd871..387236a 100644 --- a/src/jalview/gui/IProgressIndicator.java +++ b/src/jalview/gui/IProgressIndicator.java @@ -20,6 +20,8 @@ */ package jalview.gui; +import javax.swing.JProgressBar; + /** * Visual progress indicator interface. * @@ -56,4 +58,11 @@ public interface IProgressIndicator */ boolean operationInProgress(); + /** + * + * @param id + * @return progressbar mapped to id + */ + public JProgressBar getProgressBar(long id); + } diff --git a/src/jalview/gui/PCAPanel.java b/src/jalview/gui/PCAPanel.java index 576f3b2..091f79e 100644 --- a/src/jalview/gui/PCAPanel.java +++ b/src/jalview/gui/PCAPanel.java @@ -33,6 +33,7 @@ import java.awt.print.PrinterJob; import javax.swing.ButtonGroup; import javax.swing.JMenuItem; +import javax.swing.JProgressBar; import javax.swing.JRadioButtonMenuItem; import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; @@ -778,4 +779,10 @@ public class PCAPanel extends GPCAPanel getRotatableCanvas().ap = panel; PaintRefresher.Register(PCAPanel.this, panel.av.getSequenceSetId()); } + + @Override + public JProgressBar getProgressBar(long id) + { + return progressBar.getProgressBar(id); + } } diff --git a/src/jalview/gui/PaSiMapPanel.java b/src/jalview/gui/PaSiMapPanel.java new file mode 100644 index 0000000..562605e --- /dev/null +++ b/src/jalview/gui/PaSiMapPanel.java @@ -0,0 +1,861 @@ +/* + * 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.gui; + +import jalview.analysis.scoremodels.ScoreModels; +import jalview.api.AlignViewportI; +import jalview.api.analysis.ScoreModelI; +import jalview.api.analysis.SimilarityParamsI; +import jalview.bin.Console; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.AlignmentView; +import jalview.datamodel.HiddenColumns; +import jalview.datamodel.SequenceI; +import jalview.gui.ImageExporter.ImageWriterI; +import jalview.gui.JalviewColourChooser.ColourChooserListener; +import jalview.jbgui.GPaSiMapPanel; +import jalview.math.RotatableMatrix.Axis; +import jalview.util.ImageMaker; +import jalview.util.MessageManager; +import jalview.viewmodel.AlignmentViewport; +import jalview.viewmodel.PaSiMapModel; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import javax.swing.ButtonGroup; +import javax.swing.JMenuItem; +import javax.swing.JProgressBar; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.event.InternalFrameAdapter; +import javax.swing.event.InternalFrameEvent; + +/** + * The panel holding the Pairwise Similarity Map 3-D visualisation + */ +public class PaSiMapPanel extends GPaSiMapPanel + implements Runnable, IProgressIndicator +{ + private static final int MIN_WIDTH = 470; + + private static final int MIN_HEIGHT = 250; + + private final int GAP_OPEN_COST = 100; + + private final int GAP_EXTEND_COST = 5; + + private RotatableCanvas rc; + + AlignmentPanel ap; + + AlignmentViewport av; + + private PaSiMapModel pasimapModel; + + private int top = 0; + + private IProgressIndicator progressBar; + + private long progId; + + private boolean working; + + /** + * Constructor given sequence data, a similarity (or distance) score model + * name, and score calculation parameters + * + * @param alignPanel + * @param modelName + * @param params + */ + public PaSiMapPanel(AlignmentPanel alignPanel, String modelName, + SimilarityParamsI params) + { + super(8); // dim = 8 + this.av = alignPanel.av; + this.ap = alignPanel; + boolean nucleotide = av.getAlignment().isNucleotide(); + + //progressBar = new ProgressBar(statusPanel, statusBar); + + addInternalFrameListener(new InternalFrameAdapter() + { + @Override + public void internalFrameClosed(InternalFrameEvent e) + { + close_actionPerformed(); + } + }); + + boolean selected = av.getSelectionGroup() != null + && av.getSelectionGroup().getSize() > 0; + SequenceI[] seqs; + if (!selected) + { + seqs = av.getAlignment().getSequencesArray(); + } + else + { + seqs = av.getSelectionGroup().getSequencesInOrder(av.getAlignment()); + } + + ScoreModelI scoreModel = ScoreModels.getInstance() + .getScoreModel(modelName, ap); + setPasimapModel( + new PaSiMapModel(av, seqs, nucleotide, scoreModel)); + PaintRefresher.Register(this, av.getSequenceSetId()); + + setRotatableCanvas(new RotatableCanvas(alignPanel)); + this.getContentPane().add(getRotatableCanvas(), BorderLayout.CENTER); + + addKeyListener(getRotatableCanvas()); + validate(); + } + + /** + * Ensure references to potentially very large objects (the PaSiMap matrices) are + * nulled when the frame is closed + */ + protected void close_actionPerformed() + { + setPasimapModel(null); + if (this.rc != null) + { + this.rc.sequencePoints = null; + this.rc.setAxisEndPoints(null); + this.rc = null; + } + } + + @Override + protected void bgcolour_actionPerformed() + { + String ttl = MessageManager.getString("label.select_background_colour"); + ColourChooserListener listener = new ColourChooserListener() + { + @Override + public void colourSelected(Color c) + { + rc.setBgColour(c); + rc.repaint(); + } + }; + JalviewColourChooser.showColourChooser(this, ttl, rc.getBgColour(), + listener); + } + + /** + * Calculates the PaSiMap and displays the results + */ + @Override + public void run() + { + working = true; + progId = System.currentTimeMillis(); + progressBar = this; + String message = MessageManager.getString("label.pasimap_recalculating"); + if (getParent() == null) + { + progressBar = ap.alignFrame; + message = MessageManager.getString("label.pasimap_calculating"); + } + progressBar.setProgressBar(message, progId); + try + { + //&! remove big seqs + for (SequenceI seq : av.getAlignment().getSequencesArray()) + { + if (seq.getLength() > 20000) + { + //TODO add warning dialog + av.getAlignment().deleteSequence(seq); + } + } + + PairwiseAlignPanel pap = new PairwiseAlignPanel(av, true, GAP_OPEN_COST, GAP_EXTEND_COST, false); +System.out.println(pap != null); + setPairwiseAlignPanel(pap); + getPasimapModel().calculate(pap); + + xCombobox.setSelectedIndex(0); + yCombobox.setSelectedIndex(1); + zCombobox.setSelectedIndex(2); + + getPasimapModel().updateRc(getRotatableCanvas()); + // rc.invalidate(); + setTop(getPasimapModel().getTop()); + + } catch (OutOfMemoryError er) + { + new OOMWarning("calculating PaSiMap", er); + working = false; + return; + } finally + { + progressBar.setProgressBar("", progId); + } + + repaint(); + if (getParent() == null) + { + Desktop.addInternalFrame(this, + MessageManager.formatMessage("label.calc_title", "PaSiMap", + getPasimapModel().getScoreModelName()), + 475, 450); + this.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT)); + } + working = false; + } + + /** + * Updates the PaSiMap display after a change of component to use for x, y or z + * axis + */ + @Override + protected void doDimensionChange() + { + if (getTop() == 0) + { + return; + } + + int dim1 = getTop() - xCombobox.getSelectedIndex(); + int dim2 = getTop() - yCombobox.getSelectedIndex(); + int dim3 = getTop() - zCombobox.getSelectedIndex(); + getPasimapModel().updateRcView(dim1, dim2, dim3); + getRotatableCanvas().resetView(); + } + + /** + * Sets the selected checkbox item index for PaSiMap dimension (1, 2, 3...) for + * the given axis (X/Y/Z) + * + * @param index + * @param axis + */ + public void setSelectedDimensionIndex(int index, Axis axis) + { + switch (axis) + { + case X: + xCombobox.setSelectedIndex(index); + break; + case Y: + yCombobox.setSelectedIndex(index); + break; + case Z: + zCombobox.setSelectedIndex(index); + break; + default: + } + } + + @Override + protected void outputValues_actionPerformed() + { + CutAndPasteTransfer cap = new CutAndPasteTransfer(); + try + { + cap.setText(getPasimapModel().getDetails()); + Desktop.addInternalFrame(cap, + MessageManager.getString("label.pasimap_details"), 500, 500); + } catch (OutOfMemoryError oom) + { + new OOMWarning("opening PaSiMap details", oom); + cap.dispose(); + } + } + + @Override + protected void showLabels_actionPerformed() + { + getRotatableCanvas().showLabels(showLabels.getState()); + } + + @Override + protected void print_actionPerformed() + { + PaSiMapPrinter printer = new PaSiMapPrinter(); + printer.start(); + } + + /** + * If available, shows the data which formed the inputs for the PaSiMap as a new + * alignment + */ + @Override + public void originalSeqData_actionPerformed() + { + // JAL-2647 disabled after load from project (until save to project done) + if (getPasimapModel().getInputData() == null) + { + Console.info( + "Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action."); + return; + } + // decide if av alignment is sufficiently different to original data to + // warrant a new window to be created + // create new alignment window with hidden regions (unhiding hidden regions + // yields unaligned seqs) + // or create a selection box around columns in alignment view + // test Alignment(SeqCigar[]) + char gc = '-'; + try + { + // we try to get the associated view's gap character + // but this may fail if the view was closed... + gc = av.getGapCharacter(); + } catch (Exception ex) + { + } + + Object[] alAndColsel = getPasimapModel().getInputData() + .getAlignmentView(false).getAlignmentAndHiddenColumns(gc); + + if (alAndColsel != null && alAndColsel[0] != null) + { + // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]); + + AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]); + AlignmentI dataset = (av != null && av.getAlignment() != null) + ? av.getAlignment().getDataset() + : null; + if (dataset != null) + { + al.setDataset(dataset); + } + + if (true) + { + // make a new frame! + AlignFrame af = new AlignFrame(al, (HiddenColumns) alAndColsel[1], + AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); + + // >>>This is a fix for the moment, until a better solution is + // found!!<<< + // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer()); + + // af.addSortByOrderMenuItem(ServiceName + " Ordering", + // msaorder); + + Desktop.addInternalFrame(af, MessageManager.formatMessage( + "label.original_data_for_params", new String[] + { this.title }), AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + } + } + /* + * CutAndPasteTransfer cap = new CutAndPasteTransfer(); for (int i = 0; i < + * seqs.length; i++) { cap.appendText(new jalview.util.Format("%-" + 15 + + * "s").form( seqs[i].getName())); cap.appendText(" " + seqstrings[i] + + * "\n"); } + * + * Desktop.addInternalFrame(cap, "Original Data", 400, 400); + */ + } + + class PaSiMapPrinter extends Thread implements Printable + { + @Override + public void run() + { + PrinterJob printJob = PrinterJob.getPrinterJob(); + PageFormat defaultPage = printJob.defaultPage(); + PageFormat pf = printJob.pageDialog(defaultPage); + + if (defaultPage == pf) + { + /* + * user cancelled + */ + return; + } + + printJob.setPrintable(this, pf); + + if (printJob.printDialog()) + { + try + { + printJob.print(); + } catch (Exception PrintException) + { + PrintException.printStackTrace(); + } + } + } + + @Override + public int print(Graphics pg, PageFormat pf, int pi) + throws PrinterException + { + pg.translate((int) pf.getImageableX(), (int) pf.getImageableY()); + + getRotatableCanvas().drawBackground(pg); + getRotatableCanvas().drawScene(pg); + if (getRotatableCanvas().drawAxes) + { + getRotatableCanvas().drawAxes(pg); + } + + if (pi == 0) + { + return Printable.PAGE_EXISTS; + } + else + { + return Printable.NO_SUCH_PAGE; + } + } + } + + public void makePaSiMapImage(ImageMaker.TYPE type) throws Exception + { + int width = getRotatableCanvas().getWidth(); + int height = getRotatableCanvas().getHeight(); + ImageWriterI writer = new ImageWriterI() + { + @Override + public void exportImage(Graphics g) throws Exception + { + RotatableCanvas canvas = getRotatableCanvas(); + canvas.drawBackground(g); + canvas.drawScene(g); + if (canvas.drawAxes) + { + canvas.drawAxes(g); + } + } + }; + String pasimap = MessageManager.getString("label.pasimap"); + ImageExporter exporter = new ImageExporter(writer, null, type, pasimap); + exporter.doExport(null, this, width, height, pasimap); + } + + @Override + protected void viewMenu_menuSelected() + { + buildAssociatedViewMenu(); + } + + /** + * Builds the menu showing the choice of possible views (for the associated + * sequence data) to which the PaSiMap may be linked + */ + void buildAssociatedViewMenu() + { + AlignmentPanel[] aps = PaintRefresher + .getAssociatedPanels(av.getSequenceSetId()); + if (aps.length == 1 && getRotatableCanvas().av == aps[0].av) + { + associateViewsMenu.setVisible(false); + return; + } + + associateViewsMenu.setVisible(true); + + if ((viewMenu + .getItem(viewMenu.getItemCount() - 2) instanceof JMenuItem)) + { + viewMenu.insertSeparator(viewMenu.getItemCount() - 1); + } + + associateViewsMenu.removeAll(); + + JRadioButtonMenuItem item; + ButtonGroup buttonGroup = new ButtonGroup(); + int iSize = aps.length; + + for (int i = 0; i < iSize; i++) + { + final AlignmentPanel panel = aps[i]; + item = new JRadioButtonMenuItem(panel.av.getViewName(), + panel.av == getRotatableCanvas().av); + buttonGroup.add(item); + item.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent evt) + { + selectAssociatedView(panel); + } + }); + + associateViewsMenu.add(item); + } + + final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem( + "All Views"); + + buttonGroup.add(itemf); + + itemf.setSelected(getRotatableCanvas().isApplyToAllViews()); + itemf.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent evt) + { + getRotatableCanvas().setApplyToAllViews(itemf.isSelected()); + } + }); + associateViewsMenu.add(itemf); + + } + + /* + * (non-Javadoc) + * + * @see + * jalview.jbgui.GPaSiMapPanel#outputPoints_actionPerformed(java.awt.event.ActionEvent + * ) + */ + @Override + protected void outputPoints_actionPerformed() + { + CutAndPasteTransfer cap = new CutAndPasteTransfer(); + try + { + cap.setText(getPasimapModel().getPointsasCsv(false, + xCombobox.getSelectedIndex(), yCombobox.getSelectedIndex(), + zCombobox.getSelectedIndex())); + Desktop.addInternalFrame(cap, MessageManager + .formatMessage("label.points_for_params", new String[] + { this.getTitle() }), 500, 500); + } catch (OutOfMemoryError oom) + { + new OOMWarning("exporting PaSiMap points", oom); + cap.dispose(); + } + } + + /* + * (non-Javadoc) + * + * @see + * jalview.jbgui.GPaSiMapPanel#outputProjPoints_actionPerformed(java.awt.event + * .ActionEvent) + */ + @Override + protected void outputProjPoints_actionPerformed() + { + CutAndPasteTransfer cap = new CutAndPasteTransfer(); + try + { + cap.setText(getPasimapModel().getPointsasCsv(true, + xCombobox.getSelectedIndex(), yCombobox.getSelectedIndex(), + zCombobox.getSelectedIndex())); + Desktop.addInternalFrame(cap, MessageManager.formatMessage( + "label.transformed_points_for_params", new String[] + { this.getTitle() }), 500, 500); + } catch (OutOfMemoryError oom) + { + new OOMWarning("exporting transformed PaSiMap points", oom); + cap.dispose(); + } + } + + /* + * (non-Javadoc) + * + * @see + * jalview.jbgui.GPaSiMapPanel#outputAlignment_actionPerformed(java.awt.event + * .ActionEvent) + */ + @Override + protected void outputAlignment_actionPerformed() + { + CutAndPasteTransfer cap = new CutAndPasteTransfer(); + try + { + cap.setText(getPasimapModel().getAlignmentOutput()); + Desktop.addInternalFrame(cap, MessageManager.formatMessage( + "label.pairwise_alignment_for_params", new String[] { this.getTitle() }), 500, 500); + } catch (OutOfMemoryError oom) + { + new OOMWarning("exporting pairwise alignments", oom); + cap.dispose(); + } + } + + /* + * (non-Javadoc) + * + * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long) + */ + @Override + public void setProgressBar(String message, long id) + { + progressBar.setProgressBar(message, id); + // if (progressBars == null) + // { + // progressBars = new Hashtable(); + // progressBarHandlers = new Hashtable(); + // } + // + // JPanel progressPanel; + // Long lId = Long.valueOf(id); + // GridLayout layout = (GridLayout) statusPanel.getLayout(); + // if (progressBars.get(lId) != null) + // { + // progressPanel = (JPanel) progressBars.get(Long.valueOf(id)); + // statusPanel.remove(progressPanel); + // progressBars.remove(lId); + // progressPanel = null; + // if (message != null) + // { + // statusBar.setText(message); + // } + // if (progressBarHandlers.contains(lId)) + // { + // progressBarHandlers.remove(lId); + // } + // layout.setRows(layout.getRows() - 1); + // } + // else + // { + // progressPanel = new JPanel(new BorderLayout(10, 5)); + // + // JProgressBar progressBar = new JProgressBar(); + // progressBar.setIndeterminate(true); + // + // progressPanel.add(new JLabel(message), BorderLayout.WEST); + // progressPanel.add(progressBar, BorderLayout.CENTER); + // + // layout.setRows(layout.getRows() + 1); + // statusPanel.add(progressPanel); + // + // progressBars.put(lId, progressPanel); + // } + // // update GUI + // // setMenusForViewport(); + // validate(); + } + + /* + * make the progressBar determinate and update its progress + */ + public void updateProgressBar(int lengthOfTask, int progress) + { + JProgressBar pBar = progressBar.getProgressBar(progId); + if (pBar.isIndeterminate()) + { + pBar.setMaximum(lengthOfTask); + pBar.setValue(0); + pBar.setIndeterminate(false); + } + updateProgressBar(progress); + } + public void updateProgressBar(int progress) + { + JProgressBar pBar = progressBar.getProgressBar(progId); + pBar.setValue(progress); + pBar.repaint(); + } + + //&! + public void setPairwiseAlignPanel(PairwiseAlignPanel pap) + { + pap.addPropertyChangeListener(new PropertyChangeListener() + { + @Override + public void propertyChange(PropertyChangeEvent pcEvent) + { + if (PairwiseAlignPanel.PROGRESS.equals(pcEvent.getPropertyName())) + { + updateProgressBar((int) pcEvent.getNewValue()); + } else if (PairwiseAlignPanel.TOTAL.equals(pcEvent.getPropertyName())) { + updateProgressBar((int) pcEvent.getNewValue(), 0); + } + } + }); + } + + @Override + public void registerHandler(final long id, + final IProgressIndicatorHandler handler) + { + progressBar.registerHandler(id, handler); + // if (progressBarHandlers == null || + // !progressBars.contains(Long.valueOf(id))) + // { + // throw new + // Error(MessageManager.getString("error.call_setprogressbar_before_registering_handler")); + // } + // progressBarHandlers.put(Long.valueOf(id), handler); + // final JPanel progressPanel = (JPanel) progressBars.get(Long.valueOf(id)); + // if (handler.canCancel()) + // { + // JButton cancel = new JButton( + // MessageManager.getString("action.cancel")); + // final IProgressIndicator us = this; + // cancel.addActionListener(new ActionListener() + // { + // + // @Override + // public void actionPerformed(ActionEvent e) + // { + // handler.cancelActivity(id); + // us.setProgressBar(MessageManager.formatMessage("label.cancelled_params", + // new String[]{((JLabel) progressPanel.getComponent(0)).getText()}), id); + // } + // }); + // progressPanel.add(cancel, BorderLayout.EAST); + // } + } + + /** + * + * @return true if any progress bars are still active + */ + @Override + public boolean operationInProgress() + { + return progressBar.operationInProgress(); + } + + @Override + protected void resetButton_actionPerformed() + { + int t = getTop(); + setTop(0); // ugly - prevents dimensionChanged events from being processed + xCombobox.setSelectedIndex(0); + yCombobox.setSelectedIndex(1); + setTop(t); + zCombobox.setSelectedIndex(2); + } + + /** + * Answers true if PaSiMap calculation is in progress, else false + * + * @return + */ + public boolean isWorking() + { + return working; + } + + /** + * Answers the selected checkbox item index for PaSiMap dimension for the X, Y or + * Z axis of the display + * + * @param axis + * @return + */ + public int getSelectedDimensionIndex(Axis axis) + { + switch (axis) + { + case X: + return xCombobox.getSelectedIndex(); + case Y: + return yCombobox.getSelectedIndex(); + default: + return zCombobox.getSelectedIndex(); + } + } + + public void setShowLabels(boolean show) + { + showLabels.setSelected(show); + } + + /** + * Sets the input data used to calculate the PaSiMap. This is provided for + * 'restore from project', which does not currently support this (AL-2647), so + * sets the value to null, and hides the menu option for "Input Data...". J + * + * @param data + */ + public void setInputData(AlignmentViewport data) + { + getPasimapModel().setInputData(data); + originalSeqData.setVisible(data != null); + } + + public AlignViewportI getAlignViewport() + { + return av; + } + + public PaSiMapModel getPasimapModel() + { + return pasimapModel; + } + + public void setPasimapModel(PaSiMapModel pasimapModel) + { + this.pasimapModel = pasimapModel; + } + + public RotatableCanvas getRotatableCanvas() + { + return rc; + } + + public void setRotatableCanvas(RotatableCanvas rc) + { + this.rc = rc; + } + + public int getTop() + { + return top; + } + + public void setTop(int top) + { + this.top = top; + } + + /** + * set the associated view for this PaSiMap. + * + * @param panel + */ + public void selectAssociatedView(AlignmentPanel panel) + { + getRotatableCanvas().setApplyToAllViews(false); + + ap = panel; + av = panel.av; + + getRotatableCanvas().av = panel.av; + getRotatableCanvas().ap = panel; + PaintRefresher.Register(PaSiMapPanel.this, panel.av.getSequenceSetId()); + } + + @Override + public JProgressBar getProgressBar(long id) + { + return progressBar.getProgressBar(id); + } +} diff --git a/src/jalview/gui/PairwiseAlignPanel.java b/src/jalview/gui/PairwiseAlignPanel.java index b5b6ffc..af7913b 100755 --- a/src/jalview/gui/PairwiseAlignPanel.java +++ b/src/jalview/gui/PairwiseAlignPanel.java @@ -28,9 +28,12 @@ import jalview.datamodel.SequenceI; import jalview.jbgui.GPairwiseAlignPanel; import jalview.util.MessageManager; import jalview.viewmodel.AlignmentViewport; +import jalview.math.MiscMath; +import java.beans.PropertyChangeListener; import java.awt.event.ActionEvent; import java.util.Vector; +import javax.swing.event.SwingPropertyChangeSupport; /** * DOCUMENT ME! @@ -43,56 +46,113 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel private static final String DASHES = "---------------------\n"; + private float[][] scores; + + private float[][] alignmentScores; // scores used by PaSiMap + + private int GAP_OPEN_COST; + + private int GAP_EXTEND_COST; + AlignmentViewport av; Vector sequences; + private String alignmentOutput; + + private boolean suppressTextbox; + + private boolean discardAlignments; + + private boolean endGaps; + + // for listening + public static final String TOTAL = "total"; + + public static final String PROGRESS = "progress"; + + private int total; + + private int progress; + /** * Creates a new PairwiseAlignPanel object. * * @param viewport * DOCUMENT ME! + * @param endGaps ~ toggle gaps and the beginning and end of sequences */ public PairwiseAlignPanel(AlignmentViewport viewport) { + this(viewport, false, 120, 20, true); // default penalties used in AlignSeq + } + public PairwiseAlignPanel(AlignmentViewport viewport, boolean endGaps, int gapOpenCost, int gapExtendCost) + { + this(viewport, endGaps, gapOpenCost, gapExtendCost, true); + } + public PairwiseAlignPanel(AlignmentViewport viewport, boolean endGaps, int gapOpenCost, int gapExtendCost, boolean run) + { super(); this.av = viewport; + this.GAP_OPEN_COST = gapOpenCost; + this.GAP_EXTEND_COST = gapExtendCost; + this.endGaps = endGaps; + this.total = MiscMath.combinations(av.getAlignment().getHeight(), 2); + + if (run) + calculate(); +System.out.println("Creating pap"); + } + + public void calculate() + { + + SequenceGroup selectionGroup = av.getSelectionGroup(); + StringBuilder sb = new StringBuilder(1024); sequences = new Vector(); - SequenceGroup selectionGroup = viewport.getSelectionGroup(); boolean isSelection = selectionGroup != null && selectionGroup.getSize() > 0; - AlignmentView view = viewport.getAlignmentView(isSelection); - // String[] seqStrings = viewport.getViewAsString(true); + AlignmentView view = av.getAlignmentView(isSelection); + // String[] seqStrings = av.getViewAsString(true); String[] seqStrings = view - .getSequenceStrings(viewport.getGapCharacter()); + .getSequenceStrings(av.getGapCharacter()); SequenceI[] seqs; if (isSelection) { seqs = (SequenceI[]) view - .getAlignmentAndHiddenColumns(viewport.getGapCharacter())[0]; + .getAlignmentAndHiddenColumns(av.getGapCharacter())[0]; } else { seqs = av.getAlignment().getSequencesArray(); } - String type = (viewport.getAlignment().isNucleotide()) ? AlignSeq.DNA + String type = (av.getAlignment().isNucleotide()) ? AlignSeq.DNA : AlignSeq.PEP; float[][] scores = new float[seqs.length][seqs.length]; + float[][] alignmentScores = new float[seqs.length][seqs.length]; double totscore = 0D; int count = seqs.length; boolean first = true; + progress = 0; + firePropertyChange(TOTAL, 0, total); + + suppressTextbox = count<10; + discardAlignments = count<15; + for (int i = 1; i < count; i++) { + // fill diagonal alignmentScores with Float.NaN + alignmentScores[i - 1][i - 1] = Float.NaN; for (int j = 0; j < i; j++) { AlignSeq as = new AlignSeq(seqs[i], seqStrings[i], seqs[j], - seqStrings[j], type); + seqStrings[j], type, GAP_OPEN_COST, GAP_EXTEND_COST); if (as.s1str.length() == 0 || as.s2str.length() == 0) { @@ -100,28 +160,68 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel } as.calcScoreMatrix(); - as.traceAlignment(); + if (endGaps) + { + as.traceAlignmentWithEndGaps(); + } else { + as.traceAlignment(); + } + as.scoreAlignment(); if (!first) { jalview.bin.Console.outPrintln(DASHES); textarea.append(DASHES); + sb.append(DASHES); } first = false; - as.printAlignment(System.out); + if (discardAlignments) { + as.printAlignment(System.out); + } scores[i][j] = as.getMaxScore() / as.getASeq1().length; + alignmentScores[i][j] = as.getAlignmentScore(); totscore = totscore + scores[i][j]; - textarea.append(as.getOutput()); - sequences.add(as.getAlignedSeq1()); - sequences.add(as.getAlignedSeq2()); + if (suppressTextbox) + { + textarea.append(as.getOutput()); + sb.append(as.getOutput()); + } + if (discardAlignments) + { + sequences.add(as.getAlignedSeq1()); + sequences.add(as.getAlignedSeq2()); + } + + firePropertyChange(PROGRESS, progress, ++progress); } } + alignmentScores[count - 1][count - 1] = Float.NaN; + + this.scores = scores; + this.alignmentScores = alignmentScores; if (count > 2) { printScoreMatrix(seqs, scores, totscore); } + + alignmentOutput = sb.toString(); + } + + public float[][] getScores() + { + return this.scores; + } + + public float[][] getAlignmentScores() + { + return this.alignmentScores; + } + + public String getAlignmentOutput() + { + return this.alignmentOutput; } /** @@ -192,4 +292,14 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel MessageManager.getString("label.pairwise_aligned_sequences"), AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); } + + public long getTotal() + { + return total; + } + + public long getProgress() + { + return progress; + } } diff --git a/src/jalview/gui/ProgressBar.java b/src/jalview/gui/ProgressBar.java index d68d95f..8189313 100644 --- a/src/jalview/gui/ProgressBar.java +++ b/src/jalview/gui/ProgressBar.java @@ -267,4 +267,17 @@ public class ProgressBar implements IProgressIndicator }); } + /* + * + */ + public JProgressBar getProgressBar(long id) + { + for (Component component : progressBars.get(id).getComponents()) + { + if (component.getClass().equals(JProgressBar.class)) + return (JProgressBar) component; + } + return null; + } + } diff --git a/src/jalview/gui/RotatableCanvas.java b/src/jalview/gui/RotatableCanvas.java index ef0b2aa..042f178 100755 --- a/src/jalview/gui/RotatableCanvas.java +++ b/src/jalview/gui/RotatableCanvas.java @@ -119,10 +119,10 @@ public class RotatableCanvas extends JPanel private Point[] axisEndPoints; // fields for 'select rectangle' (JAL-1124) - // int rectx1; - // int recty1; - // int rectx2; - // int recty2; + int rectx1; + int recty1; + int rectx2; + int recty2; AlignmentViewport av; @@ -405,11 +405,11 @@ public class RotatableCanvas extends JPanel } } // //Now the rectangle - // if (rectx2 != -1 && recty2 != -1) { - // g.setColor(Color.white); - // - // g.drawRect(rectx1,recty1,rectx2-rectx1,recty2-recty1); - // } + if (rectx2 != -1 && recty2 != -1) { + g.setColor(Color.white); + + g.drawRect(rectx1,recty1,rectx2-rectx1,recty2-recty1); + } } /** @@ -511,10 +511,10 @@ public class RotatableCanvas extends JPanel // Cache.warn("DEBUG: Rectangle selection"); // todo not yet enabled as rectx2, recty2 are always -1 // need to set them in mouseDragged; JAL-1124 - // if ((rectx2 != -1) && (recty2 != -1)) - // { - // rectSelect(rectx1, recty1, rectx2, recty2); - // } + if ((rectx2 != -1) && (recty2 != -1)) + { + rectSelect(rectx1, recty1, rectx2, recty2); + } } repaint(); @@ -564,10 +564,10 @@ public class RotatableCanvas extends JPanel mouseX = x; mouseY = y; - // rectx1 = x; - // recty1 = y; - // rectx2 = -1; - // recty2 = -1; + rectx1 = x; + recty1 = y; + rectx2 = -1; + recty2 = -1; SequenceI found = findSequenceAtPoint(x, y); @@ -632,8 +632,8 @@ public class RotatableCanvas extends JPanel // Check if this is a rectangle drawing drag if ((evt.getModifiersEx() & InputEvent.BUTTON2_DOWN_MASK) != 0) { - // rectx2 = evt.getX(); - // recty2 = evt.getY(); + rectx2 = evt.getX(); + recty2 = evt.getY(); } else { @@ -749,15 +749,25 @@ public class RotatableCanvas extends JPanel { SequencePoint sp = sequencePoints.get(i); int tmp1 = (int) (((sp.coord.x - centre[0]) * getScaleFactor()) + * (getWidth() / 3.15) + (getWidth() / 2.0)); + float pre1 = ((sp.coord.x - centre[0]) * getScaleFactor()); int tmp2 = (int) (((sp.coord.y - centre[1]) * getScaleFactor()) + * (getHeight() / 1.70) + (getHeight() / 2.0)); + float pre2 = ((sp.coord.y - centre[1]) * getScaleFactor()); if ((tmp1 > x1) && (tmp1 < x2) && (tmp2 > y1) && (tmp2 < y2)) { if (av != null) { SequenceI sequence = sp.getSequence(); + if (av.getSelectionGroup() == null) + { + SequenceGroup sg = new SequenceGroup(); + sg.setEndRes(av.getAlignment().getWidth() - 1); + av.setSelectionGroup(sg); + } if (!av.getSelectionGroup().getSequences(null).contains(sequence)) { av.getSelectionGroup().addSequence(sequence, true); diff --git a/src/jalview/gui/StructureChooser.java b/src/jalview/gui/StructureChooser.java index 0e834d4..9658a92 100644 --- a/src/jalview/gui/StructureChooser.java +++ b/src/jalview/gui/StructureChooser.java @@ -40,6 +40,7 @@ import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; +import javax.swing.JProgressBar; import javax.swing.JTable; import javax.swing.SwingUtilities; import javax.swing.table.AbstractTableModel; @@ -1892,4 +1893,10 @@ public class StructureChooser extends GStructureChooser } } + + @Override + public JProgressBar getProgressBar(long id) + { + return progressBar.getProgressBar(id); + } } diff --git a/src/jalview/gui/WebserviceInfo.java b/src/jalview/gui/WebserviceInfo.java index 961caa7..e464b2b 100644 --- a/src/jalview/gui/WebserviceInfo.java +++ b/src/jalview/gui/WebserviceInfo.java @@ -38,6 +38,7 @@ import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JInternalFrame; import javax.swing.JPanel; +import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTextArea; @@ -945,4 +946,10 @@ public class WebserviceInfo extends GWebserviceInfo { return progressBar.operationInProgress(); } + + @Override + public JProgressBar getProgressBar(long id) + { + return progressBar.getProgressBar(id); + } } diff --git a/src/jalview/jbgui/GCutAndPasteTransfer.java b/src/jalview/jbgui/GCutAndPasteTransfer.java index 3cd52ae..5daea78 100755 --- a/src/jalview/jbgui/GCutAndPasteTransfer.java +++ b/src/jalview/jbgui/GCutAndPasteTransfer.java @@ -60,6 +60,8 @@ public class GCutAndPasteTransfer extends JInternalFrame JMenuItem pasteMenu = new JMenuItem(); + JMenuItem fontSizeMenu = new JMenuItem(); + BorderLayout borderLayout2 = new BorderLayout(); protected JPanel inputButtonPanel = new JPanel(); @@ -190,6 +192,15 @@ public class GCutAndPasteTransfer extends JInternalFrame pasteMenu_actionPerformed(e); } }); + fontSizeMenu.setText(MessageManager.getString("action.change_font")); + fontSizeMenu.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + fontSizeMenu_actionPerformed(e); + } + }); copyItem.setText(MessageManager.getString("action.copy")); copyItem.addActionListener(new ActionListener() { @@ -205,6 +216,7 @@ public class GCutAndPasteTransfer extends JInternalFrame editMenu.add(selectAll); editMenu.add(copyItem); editMenu.add(pasteMenu); + editMenu.add(fontSizeMenu); this.getContentPane().add(scrollPane, java.awt.BorderLayout.CENTER); inputButtonPanel.add(ok); inputButtonPanel.add(cancel); @@ -243,6 +255,14 @@ public class GCutAndPasteTransfer extends JInternalFrame } /** + * shows a menu for changing the font + * @param e + */ + public void fontSizeMenu_actionPerformed(ActionEvent e) + { + } + + /** * DOCUMENT ME! * * @param e diff --git a/src/jalview/jbgui/GPCAPanel.java b/src/jalview/jbgui/GPCAPanel.java index cf7bc4e..ec5b209 100755 --- a/src/jalview/jbgui/GPCAPanel.java +++ b/src/jalview/jbgui/GPCAPanel.java @@ -85,6 +85,23 @@ public class GPCAPanel extends JInternalFrame zCombobox.addItem("dim " + i); } } + public GPCAPanel(int dim) + { + try + { + jbInit(); + } catch (Exception e) + { + e.printStackTrace(); + } + + for (int i = 1; i <= dim; i++) + { + xCombobox.addItem("dim " + i); + yCombobox.addItem("dim " + i); + zCombobox.addItem("dim " + i); + } + } private void jbInit() throws Exception { diff --git a/src/jalview/jbgui/GPaSiMapPanel.java b/src/jalview/jbgui/GPaSiMapPanel.java new file mode 100755 index 0000000..369a005 --- /dev/null +++ b/src/jalview/jbgui/GPaSiMapPanel.java @@ -0,0 +1,369 @@ +/* + * 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.jbgui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JComboBox; +import javax.swing.JInternalFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.event.MenuEvent; +import javax.swing.event.MenuListener; + +import jalview.util.ImageMaker.TYPE; +import jalview.util.MessageManager; + +public class GPaSiMapPanel extends JInternalFrame +{ + private static final Font VERDANA_12 = new Font("Verdana", 0, 12); + + protected JComboBox xCombobox = new JComboBox<>(); + + protected JComboBox yCombobox = new JComboBox<>(); + + protected JComboBox zCombobox = new JComboBox<>(); + + protected JMenu viewMenu = new JMenu(); + + protected JCheckBoxMenuItem showLabels = new JCheckBoxMenuItem(); + + protected JMenu associateViewsMenu = new JMenu(); + + protected JLabel statusBar = new JLabel(); + + protected JPanel statusPanel = new JPanel(); + + protected JMenuItem originalSeqData; + + /** + * Constructor + */ + public GPaSiMapPanel() + { + try + { + jbInit(); + } catch (Exception e) + { + e.printStackTrace(); + } + + for (int i = 1; i < 8; i++) + { + xCombobox.addItem("dim " + i); + yCombobox.addItem("dim " + i); + zCombobox.addItem("dim " + i); + } + } + public GPaSiMapPanel(int dim) + { + try + { + jbInit(); + } catch (Exception e) + { + e.printStackTrace(); + } + + for (int i = 1; i <= dim; i++) + { + xCombobox.addItem("dim " + i); + yCombobox.addItem("dim " + i); + zCombobox.addItem("dim " + i); + } + } + + private void jbInit() throws Exception + { + setFrameIcon(null); + setName("jalview-pca"); + this.getContentPane().setLayout(new BorderLayout()); + JPanel jPanel2 = new JPanel(); + jPanel2.setLayout(new FlowLayout()); + JLabel jLabel1 = new JLabel(); + jLabel1.setFont(VERDANA_12); + jLabel1.setText("x="); + JLabel jLabel2 = new JLabel(); + jLabel2.setFont(VERDANA_12); + jLabel2.setText("y="); + JLabel jLabel3 = new JLabel(); + jLabel3.setFont(VERDANA_12); + jLabel3.setText("z="); + jPanel2.setBackground(Color.white); + jPanel2.setBorder(null); + zCombobox.setFont(VERDANA_12); + zCombobox.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + doDimensionChange(); + } + }); + yCombobox.setFont(VERDANA_12); + yCombobox.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + doDimensionChange(); + } + }); + xCombobox.setFont(VERDANA_12); + xCombobox.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + doDimensionChange(); + } + }); + JButton resetButton = new JButton(); + resetButton.setFont(VERDANA_12); + resetButton.setText(MessageManager.getString("action.reset")); + resetButton.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + resetButton_actionPerformed(); + } + }); + JMenu fileMenu = new JMenu(); + fileMenu.setText(MessageManager.getString("action.file")); + JMenu saveMenu = new JMenu(); + saveMenu.setText(MessageManager.getString("action.save_as")); + JMenuItem eps = new JMenuItem("EPS"); + eps.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + makePCAImage(TYPE.EPS); + } + }); + JMenuItem png = new JMenuItem("PNG"); + png.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + makePCAImage(TYPE.PNG); + } + }); + JMenuItem outputValues = new JMenuItem(); + outputValues.setText(MessageManager.getString("label.output_values")); + outputValues.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + outputValues_actionPerformed(); + } + }); + JMenuItem outputPoints = new JMenuItem(); + outputPoints.setText(MessageManager.getString("label.output_points")); + outputPoints.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + outputPoints_actionPerformed(); + } + }); + JMenuItem outputProjPoints = new JMenuItem(); + outputProjPoints.setText( + MessageManager.getString("label.output_transformed_points")); + outputProjPoints.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + outputProjPoints_actionPerformed(); + } + }); + JMenuItem print = new JMenuItem(); + print.setText(MessageManager.getString("action.print")); + print.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + print_actionPerformed(); + } + }); + JMenuItem outputAlignment = new JMenuItem(); + outputAlignment.setText( + MessageManager.getString("label.output_alignment")); + outputAlignment.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + outputAlignment_actionPerformed(); + } + }); + viewMenu.setText(MessageManager.getString("action.view")); + viewMenu.addMenuListener(new MenuListener() + { + @Override + public void menuSelected(MenuEvent e) + { + viewMenu_menuSelected(); + } + + @Override + public void menuDeselected(MenuEvent e) + { + } + + @Override + public void menuCanceled(MenuEvent e) + { + } + }); + showLabels.setText(MessageManager.getString("label.show_labels")); + showLabels.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + showLabels_actionPerformed(); + } + }); + JMenuItem bgcolour = new JMenuItem(); + bgcolour.setText(MessageManager.getString("action.background_colour")); + bgcolour.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + bgcolour_actionPerformed(); + } + }); + originalSeqData = new JMenuItem(); + originalSeqData.setText(MessageManager.getString("label.input_data")); + originalSeqData.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + originalSeqData_actionPerformed(); + } + }); + associateViewsMenu.setText( + MessageManager.getString("label.associate_nodes_with")); + + statusPanel.setLayout(new GridLayout()); + statusBar.setFont(VERDANA_12); + // statusPanel.setBackground(Color.lightGray); + // statusBar.setBackground(Color.lightGray); + // statusPanel.add(statusBar, null); + JPanel panelBar = new JPanel(new BorderLayout()); + panelBar.add(jPanel2, BorderLayout.NORTH); + panelBar.add(statusPanel, BorderLayout.SOUTH); + this.getContentPane().add(panelBar, BorderLayout.SOUTH); + jPanel2.add(jLabel1, null); + jPanel2.add(xCombobox, null); + jPanel2.add(jLabel2, null); + jPanel2.add(yCombobox, null); + jPanel2.add(jLabel3, null); + jPanel2.add(zCombobox, null); + jPanel2.add(resetButton, null); + + JMenuBar jMenuBar1 = new JMenuBar(); + jMenuBar1.add(fileMenu); + jMenuBar1.add(viewMenu); + setJMenuBar(jMenuBar1); + fileMenu.add(saveMenu); + fileMenu.add(outputValues); + fileMenu.add(print); + fileMenu.add(originalSeqData); + fileMenu.add(outputPoints); + fileMenu.add(outputProjPoints); + fileMenu.add(outputAlignment); + saveMenu.add(eps); + saveMenu.add(png); + viewMenu.add(showLabels); + viewMenu.add(bgcolour); + viewMenu.add(associateViewsMenu); + } + + protected void resetButton_actionPerformed() + { + } + + protected void outputPoints_actionPerformed() + { + } + + protected void outputProjPoints_actionPerformed() + { + } + + protected void outputAlignment_actionPerformed() + { + } + + public void makePCAImage(TYPE imageType) + { + } + + protected void outputValues_actionPerformed() + { + } + + protected void print_actionPerformed() + { + } + + protected void showLabels_actionPerformed() + { + } + + protected void bgcolour_actionPerformed() + { + } + + protected void originalSeqData_actionPerformed() + { + } + + protected void viewMenu_menuSelected() + { + } + + protected void doDimensionChange() + { + } +} diff --git a/src/jalview/math/Matrix.java b/src/jalview/math/Matrix.java index ad91464..85d0db3 100755 --- a/src/jalview/math/Matrix.java +++ b/src/jalview/math/Matrix.java @@ -24,6 +24,7 @@ import jalview.util.Format; import jalview.util.MessageManager; import java.io.PrintStream; +import java.lang.Math; import java.util.Arrays; /** @@ -107,6 +108,32 @@ public class Matrix implements MatrixI } } + public Matrix(float[][] values) + { + this.rows = values.length; + this.cols = this.rows == 0 ? 0 : values[0].length; + + /* + * make a copy of the values array, for immutability + */ + this.value = new double[rows][]; + int i = 0; + for (float[] row : values) + { + if (row != null) + { + value[i] = new double[row.length]; + int j = 0; + for (float oldValue : row) + { + value[i][j] = oldValue; + j++; + } + } + i++; + } + } + @Override public MatrixI transpose() { @@ -164,7 +191,10 @@ public class Matrix implements MatrixI */ for (int k = 0; k < in.width(); k++) { - tmp[i][j] += (in.getValue(i, k) * this.value[k][j]); + if (!Double.isNaN(in.getValue(i,k)) && !Double.isNaN(this.value[k][j])) + { + tmp[i][j] += (in.getValue(i, k) * this.value[k][j]); + } } } } @@ -803,12 +833,24 @@ public class Matrix implements MatrixI } /** + * returns the matrix as a double[][] array + * + * @return + */ + @Override + public double[][] asArray() + { + return value; + } + + /** * Returns an array containing the values in the specified column * * @param col * * @return */ + @Override public double[] getColumn(int col) { double[] out = new double[rows]; @@ -831,7 +873,7 @@ public class Matrix implements MatrixI @Override public void printD(PrintStream ps, String format) { - for (int j = 0; j < rows; j++) + for (int j = 0; j < d.length; j++) { Format.print(ps, format, d[j]); } @@ -973,6 +1015,26 @@ public class Matrix implements MatrixI } } + /** + * Add d to all entries of this matrix + * + * @param d ~ value to add + */ + @Override + public void add(double d) + { + for (double[] row : value) + { + if (row != null) + { + for (int i = 0; i < row.length; i++) + { + row[i] += d; + } + } + } + } + @Override public void setD(double[] v) { @@ -1022,4 +1084,354 @@ public class Matrix implements MatrixI } return true; } + + /** + * Returns a copy in which every value in the matrix is its absolute + * + * @return + */ + @Override + public MatrixI absolute() + { + MatrixI copy = this.copy(); + for (int i = 0; i < copy.width(); i++) + { + double[] row = copy.getRow(i); + if (row != null) + { + for (int j = 0; j < row.length; j++) + { + row[j] = Math.abs(row[j]); + } + } + } + return copy; + } + + /** + * Returns the mean of each row + * + * @return + */ + @Override + public double[] meanRow() + { + double[] mean = new double[rows]; + int i = 0; + for (double[] row : value) + { + if (row != null) + { + mean[i++] = MiscMath.mean(row); + } + } + return mean; + } + + /** + * Returns the mean of each column + * + * @return + */ + @Override + public double[] meanCol() + { + double[] mean = new double[cols]; + for (int j = 0; j < cols; j++) + { + double[] column = getColumn(j); + if (column != null) + { + mean[j] = MiscMath.mean(column); + } + } + return mean; + } + + /** + * return a flattened matrix containing the sum of each column + * + * @return + */ + @Override + public double[] sumCol() + { + double[] sum = new double[cols]; + for (int j = 0; j < cols; j++) + { + double[] column = getColumn(j); + if (column != null) + { + sum[j] = MiscMath.sum(column); + } + } + return sum; + } + + /** + * returns the mean value of the complete matrix + * + * @return + */ + @Override + public double mean() + { + double sum = 0; + int nanCount = 0; + for (double[] row : value) + { + for (double col : row) + { + if (!Double.isNaN(col)) + { + sum += col; + } else { + nanCount++; + } + } + } + return sum / (double) (this.rows * this.cols - nanCount); + } + + /** + * fills up a diagonal matrix with its transposed copy + * !other side should be filled with 0 + * !keeps Double.NaN found in either side + * + * TODO check on which side it was diagonal and only do calculations for the other side + */ + @Override + public void fillDiagonal() + { + int n = this.rows; + int m = this.cols; + MatrixI copy = this.transpose(); // goes through each element in the matrix and + for (int i = 0; i < n; i++) // adds the value in the transposed copy to the original value + { + for (int j = 0; j < m; j++) + { + if (i != j) + { + this.addValue(i, j, copy.getValue(i,j)); + } + } + } + } + + /** + * counts the number of Double.NaN in the matrix + * + * @return + */ + @Override + public int countNaN() + { + int NaN = 0; + for (int i = 0; i < this.rows; i++) + { + for (int j = 0; j < this.cols; j++) + { + if (Double.isNaN(this.getValue(i,j))) + { + NaN++; + } + } + } + return NaN; + } + + /** + * performs an element-wise addition of this matrix by another matrix ~ this - m + * @param m ~ other matrix + * + * @return + */ + @Override + public MatrixI add(MatrixI m) + { + if (m.width() != cols || m.height() != rows) + { + throw new IllegalArgumentException("Can't add a " + m.height() + "x" + m.width() + " to a " + this.rows + "x" + this.cols + " matrix"); + } + double[][] tmp = new double[this.rows][this.cols]; + for (int i = 0; i < this.rows; i++) + { + for (int j = 0; j < this.cols; j++) + { + tmp[i][j] = this.getValue(i,j) + m.getValue(i,j); + } + } + return new Matrix(tmp); + } + + /** + * performs an element-wise subtraction of this matrix by another matrix ~ this - m + * @param m ~ other matrix + * + * @return + */ + @Override + public MatrixI subtract(MatrixI m) + { + if (m.width() != cols || m.height() != rows) + { + throw new IllegalArgumentException("Can't subtract a " + m.height() + "x" + m.width() + " from a " + this.rows + "x" + this.cols + " matrix"); + } + double[][] tmp = new double[this.rows][this.cols]; + for (int i = 0; i < this.rows; i++) + { + for (int j = 0; j < this.cols; j++) + { + tmp[i][j] = this.getValue(i,j) - m.getValue(i,j); + } + } + return new Matrix(tmp); + } + + /** + * performs an element-wise multiplication of this matrix by another matrix ~ this * m + * @param m ~ other matrix + * + * @return + */ + @Override + public MatrixI elementwiseMultiply(MatrixI m) + { + if (m.width() != cols || m.height() != rows) + { + throw new IllegalArgumentException("Can't multiply a " + this.rows + "x" + this.cols + " by a " + m.height() + "x" + m.width() + " matrix"); + } + double[][] tmp = new double[this.rows][this.cols]; + for (int i = 0; i < this.rows; i++) + { + for (int j = 0; j < this.cols; j++) + { + tmp[i][j] = this.getValue(i, j) * m.getValue(i,j); + } + } + return new Matrix(tmp); + } + + /** + * performs an element-wise division of this matrix by another matrix ~ this / m + * @param m ~ other matrix + * + * @return + */ + @Override + public MatrixI elementwiseDivide(MatrixI m) + { + if (m.width() != cols || m.height() != rows) + { + throw new IllegalArgumentException("Can't divide a " + this.rows + "x" + this.cols + " by a " + m.height() + "x" + m.width() + " matrix"); + } + double[][] tmp = new double[this.rows][this.cols]; + for (int i = 0; i < this.rows; i++) + { + for (int j = 0; j < this.cols; j++) + { + tmp[i][j] = this.getValue(i, j) / m.getValue(i,j); + } + } + return new Matrix(tmp); + } + + /** + * calculate the root-mean-square for tow matrices + * @param m ~ other matrix + * + * @return + */ + @Override + public double rmsd(MatrixI m) + { + MatrixI squaredDeviates = this.subtract(m); + squaredDeviates = squaredDeviates.preMultiply(squaredDeviates); + return Math.sqrt(squaredDeviates.mean()); + } + + /** + * calculates the Frobenius norm of this matrix + * + * @return + */ + @Override + public double norm() + { + double result = 0; + for (double[] row : value) + { + for (double val : row) + { + result += Math.pow(val, 2); + } + } + return Math.sqrt(result); + } + + /** + * returns the sum of all values in this matrix + * + * @return + */ + @Override + public double sum() + { + double sum = 0; + for (double[] row : value) + { + for (double val : row) + { + sum += (Double.isNaN(val)) ? 0.0 : val; + } + } + return sum; + } + + /** + * returns the sum-product of this matrix with vector v + * @param v ~ vector + * + * @return + */ + @Override + public double[] sumProduct(double[] v) + { + if (v.length != cols) + { + throw new IllegalArgumentException("Vector and matrix do not have the same dimension! (" + v.length + " != " + cols + ")"); + } + double[] result = new double[rows]; + for (int i = 0; i < rows; i++) + { + double[] row = value[i]; + double sum = 0; + for (int j = 0; j < row.length; j++) + { + sum += row[j] * v[j]; + } + result[i] = sum; + } + return result; + } + + /** + * mirrors columns of the matrix + * + * @return + */ + @Override + public MatrixI mirrorCol() + { + double[][] result = new double[rows][cols]; + for (int i = 0; i < rows; i++) + { + int k = cols - 1; // reverse col + for (int j = 0; j < cols; j++) + { + result[i][k--] = this.getValue(i,j); + } + } + return new Matrix(result); + } } diff --git a/src/jalview/math/MatrixI.java b/src/jalview/math/MatrixI.java index e98007e..7faac73 100644 --- a/src/jalview/math/MatrixI.java +++ b/src/jalview/math/MatrixI.java @@ -61,6 +61,13 @@ public interface MatrixI void setValue(int i, int j, double d); /** + * Returns the matrix as a double[][] array + * + * @return + */ + double[][] asArray(); + + /** * Answers a copy of the values in the i'th row * * @return @@ -68,6 +75,13 @@ public interface MatrixI double[] getRow(int i); /** + * Answers a copy of the values in the i'th column + * + * @return + */ + double[] getColumn(int i); + + /** * Answers a new matrix with a copy of the values in this one * * @return @@ -161,6 +175,13 @@ public interface MatrixI void multiply(double d); /** + * Add d to all entries of this matrix + * + * @param d ~ value to add + */ + void add(double d); + + /** * Answers true if the two matrices have the same dimensions, and * corresponding values all differ by no more than delta (which should be a * positive value), else false @@ -170,4 +191,127 @@ public interface MatrixI * @return */ boolean equals(MatrixI m2, double delta); + + /** + * Returns a copy in which every value in the matrix is its absolute + */ + MatrixI absolute(); + + /** + * Returns the mean of each row + */ + double[] meanRow(); + + /** + * Returns the mean of each column + */ + double[] meanCol(); + + /** + * Returns a flattened matrix containing the sum of each column + * + * @return + */ + double[] sumCol(); + + /** + * returns the mean value of the complete matrix + */ + double mean(); + + /** + * fills up a diagonal matrix with its transposed copy + * !other side should be filled with either 0 or Double.NaN + */ + void fillDiagonal(); + + /** + * counts the number of Double.NaN in the matrix + * + * @return + */ + int countNaN(); + + /** + * performs an element-wise addition of this matrix by another matrix + * !matrices have to be the same size + * @param m ~ other matrix + * + * @return + * @throws IllegalArgumentException + * if this and m do not have the same dimensions + */ + MatrixI add(MatrixI m); + + /** + * performs an element-wise subtraction of this matrix by another matrix + * !matrices have to be the same size + * @param m ~ other matrix + * + * @return + * @throws IllegalArgumentException + * if this and m do not have the same dimensions + */ + MatrixI subtract(MatrixI m); + + /** + * performs an element-wise multiplication of this matrix by another matrix ~ this * m + * !matrices have to be the same size + * @param m ~ other matrix + * + * @return + * @throws IllegalArgumentException + * if this and m do not have the same dimensions + */ + MatrixI elementwiseMultiply(MatrixI m); + + /** + * performs an element-wise division of this matrix by another matrix ~ this / m + * !matrices have to be the same size + * @param m ~ other matrix + * + * @return + * @throws IllegalArgumentException + * if this and m do not have the same dimensions + */ + MatrixI elementwiseDivide(MatrixI m); + + /** + * calculates the root-mean-square for two matrices + * @param m ~ other matrix + * + * @return + */ + double rmsd(MatrixI m); + + /** + * calculates the Frobenius norm of this matrix + * + * @return + */ + double norm(); + + /** + * returns the sum of all values in this matrix + * + * @return + */ + double sum(); + + /** + * returns the sum-product of this matrix with vector v + * @param v ~ vector + * + * @return + * @throws IllegalArgumentException + * if this.cols and v do not have the same length + */ + double[] sumProduct(double[] v); + + /** + * mirrors the columns of this matrix + * + * @return + */ + MatrixI mirrorCol(); } diff --git a/src/jalview/math/MiscMath.java b/src/jalview/math/MiscMath.java new file mode 100755 index 0000000..b5218d5 --- /dev/null +++ b/src/jalview/math/MiscMath.java @@ -0,0 +1,381 @@ +/* + * 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.math; + +import jalview.util.Format; + +import java.lang.Math; +import java.util.Arrays; + +/** + * A collection of miscellaneous mathematical operations + * @AUTHOR MorellThomas + */ +public class MiscMath +{ + /** + * prints an array + * @param m ~ array + */ + public static void print(double[] m, String format) + { + System.out.print("[ "); + for (double a : m) + { + Format.print(System.out, format + " ", a); + } + System.out.println("]"); + } + + /** + * calculates the mean of an array + * + * @param m ~ array + * @return + */ + public static double mean(double[] m) + { + double sum = 0; + int nanCount = 0; + for (int i = 0; i < m.length; i++) + { + if (!Double.isNaN(m[i])) // ignore NaN values in the array + { + sum += m[i]; + } else { + nanCount++; + } + } + return sum / (double) (m.length - nanCount); + } + + /** + * calculates the sum of an array + * + * @param m ~ array + * @return + */ + public static double sum(double[] m) + { + double sum = 0; + for (int i = 0; i < m.length; i++) + { + if (!Double.isNaN(m[i])) // ignore NaN values in the array + { + sum += m[i]; + } + } + return sum; + } + + /** + * calculates the square root of each element in an array + * + * @param m ~ array + * + * @return + * TODO + * make general with function passed -> apply function to each element + */ + public static double[] sqrt(double[] m) + { + double[] sqrts = new double[m.length]; + for (int i = 0; i < m.length; i++) + { + sqrts[i] = Math.sqrt(m[i]); + } + return sqrts; + } + + /** + * calculate element wise multiplication of two arrays with the same length + * + * @param a ~ array + * @param b ~ array + * + * @return + */ + public static double[] elementwiseMultiply(byte[] a, double[] b) throws RuntimeException + { + if (a.length != b.length) // throw exception if the arrays do not have the same length + { + throw new SameLengthException(a.length, b.length); + } + double[] result = new double[a.length]; + for (int i = 0; i < a.length; i++) + { + result[i] = a[i] * b[i]; + } + return result; + } + public static double[] elementwiseMultiply(double[] a, double[] b) throws RuntimeException + { + if (a.length != b.length) // throw exception if the arrays do not have the same length + { + throw new SameLengthException(a.length, b.length); + } + double[] result = new double[a.length]; + for (int i = 0; i < a.length; i++) + { + result[i] = a[i] * b[i]; + } + return result; + } + public static byte[] elementwiseMultiply(byte[] a, byte[] b) throws RuntimeException + { + if (a.length != b.length) // throw exception if the arrays do not have the same length + { + throw new SameLengthException(a.length, b.length); + } + byte[] result = new byte[a.length]; + for (int i = 0; i < a.length; i++) + { + result[i] = (byte) (a[i] * b[i]); + } + return result; + } + public static double[] elementwiseMultiply(double[] a, double b) + { + double[] result = new double[a.length]; + for (int i = 0; i < a.length; i++) + { + result[i] = a[i] * b; + } + return result; + } + + /** + * calculate element wise division of two arrays ~ a / b + * + * @param a ~ array + * @param b ~ array + * + * @return + */ + public static double[] elementwiseDivide(double[] a, double[] b) throws RuntimeException + { + if (a.length != b.length) // throw exception if the arrays do not have the same length + { + throw new SameLengthException(a.length, b.length); + } + double[] result = new double[a.length]; + for (int i = 0; i < a.length; i++) + { + result[i] = a[i] / b[i]; + } + return result; + } + + /** + * calculate element wise addition of two arrays + * + * @param a ~ array + * @param b ~ array + * + * @return + */ + public static double[] elementwiseAdd(double[] a, double[] b) throws RuntimeException + { + if (a.length != b.length) // throw exception if the arrays do not have the same length + { + throw new SameLengthException(a.length, b.length); + } + double[] result = new double[a.length]; + + for (int i = 0; i < a.length; i++) + { + result[i] += a[i] + b[i]; + } + return result; + } + public static double[] elementwiseAdd(double[] a, double b) + { + double[] result = new double[a.length]; + for (int i = 0; i < a.length; i++) + { + result[i] = a[i] + b; + } + return result; + } + + /** + * returns true if two arrays are element wise within a tolerance + * + * @param a ~ array + * @param b ~ array + * @param rtol ~ relative tolerance + * @param atol ~ absolute tolerance + * @param equalNAN ~ whether NaN at the same position return true + * + * @return + */ + public static boolean allClose(double[] a, double[] b, double rtol, double atol, boolean equalNAN) + { + boolean areEqual = true; + for (int i = 0; i < a.length; i++) + { + if (equalNAN && (Double.isNaN(a[i]) && Double.isNaN(b[i]))) // if equalNAN == true -> skip the NaN pair + { + continue; + } + if (Math.abs(a[i] - b[i]) > (atol + rtol * Math.abs(b[i]))) // check for the similarity condition -> if not met -> break and return false + { + areEqual = false; + break; + } + } + return areEqual; + } + + /** + * returns the index of the maximum and the maximum value of an array + * + * @param a ~ array + * + * @return + */ + public static int[] findMax(int[] a) + { + int max = 0; + int maxIndex = 0; + for (int i = 0; i < a.length; i++) + { + if (a[i] > max) + { + max = a[i]; + maxIndex = i; + } + } + return new int[]{maxIndex, max}; + } + + /** + * returns the dot product of two arrays + * @param a ~ array a + * @param b ~ array b + * + * @return + */ + public static double dot(double[] a, double[] b) + { + if (a.length != b.length) + { + throw new IllegalArgumentException(String.format("Vectors do not have the same length (%d, %d)!", a.length, b.length)); + } + + double aibi = 0; + for (int i = 0; i < a.length; i++) + { + aibi += a[i] * b[i]; + } + return aibi; + } + + /** + * returns the euklidian norm of the vector + * @param v ~ vector + * + * @return + */ + public static double norm(double[] v) + { + double result = 0; + for (double i : v) + { + result += Math.pow(i, 2); + } + return Math.sqrt(result); + } + + /** + * returns the number of NaN in the vector + * @param v ~ vector + * + * @return + */ + public static int countNaN(double[] v) + { + int cnt = 0; + for (double i : v) + { + if (Double.isNaN(i)) + { + cnt++; + } + } + return cnt; + } + + /** + * recursively calculates the permutations of total n items with r items per combination + * according to n!/(n-r)! by only multiplying the relevant terms + * @param n + * @param r + * + * @return permutations + */ + public static long permutations(int n, int r) + { + if (n < r) + return permutations(r, n); + + long result = 1l; + for (int i = 0; i < r; i++) + { + result *= (n-i); + } + return result; + } + + /** + * calculate all unique combinations of n elements into r sized groups + * @param n + * @param r + * + * @return + */ + public static int combinations(int n, int r) + { + int result = 1; + for (int i = 0; i < r; i++) + { + result *= (n-1); + } + return (int) (result / MiscMath.factorial(r)); + } + + /** + * calculate the factorial of n (n >= 0) + * @param n + * + * @return + */ + public static int factorial(int n) + { + int result = 1; + for (int i = 0; i < n; i++) + { + result *= (n - i); + } + return result; + } + +} diff --git a/src/jalview/math/SameLengthException.java b/src/jalview/math/SameLengthException.java new file mode 100644 index 0000000..9621827 --- /dev/null +++ b/src/jalview/math/SameLengthException.java @@ -0,0 +1,35 @@ +/* + * 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.math; + +public class SameLengthException extends RuntimeException +{ + public SameLengthException(int lengthA, int lengthB) + { + this("Your arrays do not have the same length!", lengthA, lengthB); + } + + public SameLengthException(String message, int lengthA, int lengthB) + { + super(String.format("%s (%d and %d)", message, lengthA, lengthB)); + } + +} diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index 712c63b..4708888 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.Map; import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder; +import jalview.analysis.Connectivity; import jalview.analysis.Conservation; import jalview.analysis.TreeModel; import jalview.api.AlignCalcManagerI; @@ -3293,4 +3294,9 @@ public abstract class AlignmentViewport + savedUpToDate); return savedUpToDate; } + + public Hashtable calculateConnectivity(float[][] scores, byte dim) + { + return Connectivity.getConnectivity(this, scores, dim); + } } diff --git a/src/jalview/viewmodel/PCAModel.java b/src/jalview/viewmodel/PCAModel.java index 1693294..71bef00 100644 --- a/src/jalview/viewmodel/PCAModel.java +++ b/src/jalview/viewmodel/PCAModel.java @@ -101,7 +101,7 @@ public class PCAModel top = height - 1; points = new Vector<>(); - Point[] scores = pca.getComponents(top - 1, top - 2, top - 3, 100); + Point[] scores = pca.getComponents(top - 1, top - 2, top - 3, 1); for (int i = 0; i < height; i++) { @@ -153,7 +153,7 @@ public class PCAModel public void updateRcView(int dim1, int dim2, int dim3) { // note: actual indices for components are dim1-1, etc (patch for JAL-1123) - Point[] scores = pca.getComponents(dim1 - 1, dim2 - 1, dim3 - 1, 100); + Point[] scores = pca.getComponents(dim1 - 1, dim2 - 1, dim3 - 1, 1); for (int i = 0; i < pca.getHeight(); i++) { diff --git a/src/jalview/viewmodel/PaSiMapModel.java b/src/jalview/viewmodel/PaSiMapModel.java new file mode 100644 index 0000000..a0e5174 --- /dev/null +++ b/src/jalview/viewmodel/PaSiMapModel.java @@ -0,0 +1,261 @@ +/* + * 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.viewmodel; + +import jalview.analysis.PaSiMap; +import jalview.api.RotatableCanvasI; +import jalview.api.analysis.ScoreModelI; +import jalview.api.analysis.SimilarityParamsI; +import jalview.datamodel.AlignmentView; +import jalview.datamodel.Point; +import jalview.datamodel.SequenceI; +import jalview.datamodel.SequencePoint; +import jalview.gui.PairwiseAlignPanel; +import jalview.viewmodel.AlignmentViewport; + +import java.util.List; +import java.util.Vector; + +public class PaSiMapModel +{ + /* + * inputs + */ + private AlignmentViewport inputData; + + private final SequenceI[] seqs; + + /* + * options - score model, nucleotide / protein + */ + private ScoreModelI scoreModel; + + private boolean nucleotide = false; + + /* + * outputs + */ + private PaSiMap pasimap; + + int top; + + private List points; + + /** + * Constructor given sequence data, score model and score calculation + * parameter options. + * + * @param seqData + * @param sqs + * @param nuc + * @param modelName + * @param params + */ + public PaSiMapModel(AlignmentViewport seqData, SequenceI[] sqs, boolean nuc, + ScoreModelI modelName) + { + inputData = seqData; + seqs = sqs; + nucleotide = nuc; + scoreModel = modelName; + } + + /** + * Performs the PaSiMap calculation (in the same thread) and extracts result data + * needed for visualisation by PaSiMapPanel + */ + public void calculate(PairwiseAlignPanel pap) + { + pasimap = new PaSiMap(inputData, scoreModel, pap); + pasimap.run(); // executes in same thread, wait for completion + + // Now find the component coordinates + int ii = 0; + + while ((ii < seqs.length) && (seqs[ii] != null)) + { + ii++; + } + + int width = pasimap.getWidth(); + int height = pasimap.getHeight(); + top = width; + + points = new Vector<>(); + Point[] scores = pasimap.getComponents(width - 1, width - 2, width - 3, 1); + + for (int i = 0; i < height; i++) + { + SequencePoint sp = new SequencePoint(seqs[i], scores[i]); + points.add(sp); + } + } + + public void updateRc(RotatableCanvasI rc) + { + rc.setPoints(points, pasimap.getHeight()); + } + + public boolean isNucleotide() + { + return nucleotide; + } + + public void setNucleotide(boolean nucleotide) + { + this.nucleotide = nucleotide; + } + + /** + * Answers the index of the principal dimension of the PaSiMap + * + * @return + */ + public int getTop() + { + return top; + } + + public void setTop(int t) + { + top = t; + } + + /** + * Updates the 3D coordinates for the list of points to the given dimensions. + * Principal dimension is getTop(). Next greatest eigenvector is getTop()-1. + * Note - pasimap.getComponents starts counting the spectrum from rank-2 to zero, + * rather than rank-1, so getComponents(dimN ...) == updateRcView(dimN+1 ..) + * + * @param dim1 + * @param dim2 + * @param dim3 + */ + public void updateRcView(int dim1, int dim2, int dim3) + { + // note: actual indices for components are dim1-1, etc (patch for JAL-1123) + Point[] scores = pasimap.getComponents(dim1 - 1, dim2 - 1, dim3 - 1, 1); + + for (int i = 0; i < pasimap.getHeight(); i++) + { + points.get(i).coord = scores[i]; + } + } + + public String getDetails() + { + return pasimap.getDetails(); + } + + public String getAlignmentOutput() + { + return pasimap.getAlignmentOutput(); + } + + public AlignmentViewport getInputData() + { + return inputData; + } + + public void setInputData(AlignmentViewport data) + { + inputData = data; + } + + public String getPointsasCsv(boolean transformed, int xdim, int ydim, + int zdim) + { + StringBuffer csv = new StringBuffer(); + csv.append("\"Sequence\""); + if (transformed) + { + csv.append(","); + csv.append(xdim); + csv.append(","); + csv.append(ydim); + csv.append(","); + csv.append(zdim); + } + else + { + for (int d = 1, dmax = (int) pasimap.getDim(); d <= dmax; d++) + { + csv.append("," + d); + } + } + csv.append("\n"); + for (int s = 0; s < seqs.length; s++) + { + csv.append("\"" + seqs[s].getName() + "\""); + if (!transformed) + { + double[] fl = pasimap.component(s); + for (int d = fl.length - 1; d >= 0; d--) + { + csv.append(","); + csv.append(fl[d]); + } + } else { + Point p = points.get(s).coord; + csv.append(",").append(p.x); + csv.append(",").append(p.y); + csv.append(",").append(p.z); + } + csv.append("\n"); + } + return csv.toString(); + } + + public String getScoreModelName() + { + return scoreModel == null ? "" : scoreModel.getName(); + } + + public void setScoreModel(ScoreModelI sm) + { + this.scoreModel = sm; + } + + public List getSequencePoints() + { + return points; + } + + public void setSequencePoints(List sp) + { + points = sp; + } + + /** + * Answers the object holding the values of the computed PaSiMap + * + * @return + */ + public PaSiMap getPasimapData() + { + return pasimap; + } + + public void setPaSiMap(PaSiMap data) + { + pasimap = data; + } +} diff --git a/test/jalview/analysis/AlignSeqTest.java b/test/jalview/analysis/AlignSeqTest.java index a9a9730..03bdd0b 100644 --- a/test/jalview/analysis/AlignSeqTest.java +++ b/test/jalview/analysis/AlignSeqTest.java @@ -76,4 +76,16 @@ public class AlignSeqTest String s = "aArRnNzZxX *.-?"; assertArrayEquals(expected, as.indexEncode(s)); } + @Test(groups= {"Functional"}) + public void testGlobalAlignment() + { + String seq1="CAGCTAGCG",seq2="CCATACGA"; + Sequence sq1=new Sequence("s1",seq1),sq2=new Sequence("s2",seq2); + // AlignSeq doesn't report the unaligned regions at either end of sequences + //String alseq1="-CAGCTAGCG-",alseq2="CCA--TA-CGA"; + // so we check we have the aligned segment correct only + String alseq1="CAGCTAGCG",alseq2="CA--TA-CG"; + AlignSeq as = AlignSeq.doGlobalNWAlignment(sq1,sq2,AlignSeq.DNA); + assertEquals(as.getAStr1()+"\n"+as.getAStr2(),alseq1+"\n"+alseq2); + } }