
de.citec.tcs.alignment.SoftPathModel Maven / Gradle / Ivy
/*
* TCS Alignment Toolbox
*
* Copyright (C) 2013-2015
* Benjamin Paaßen, Georg Zentgraf
* AG Theoretical Computer Science
* Centre of Excellence Cognitive Interaction Technology (CITEC)
* University of Bielefeld
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package de.citec.tcs.alignment;
import de.citec.tcs.alignment.comparators.DerivableComparator;
import de.citec.tcs.alignment.comparators.GapComparator;
import de.citec.tcs.alignment.comparators.OperationType;
import de.citec.tcs.alignment.comparators.SparseDerivableComparator;
import de.citec.tcs.alignment.comparators.SparseLocalDerivative;
import de.citec.tcs.alignment.parallel.MatrixEngine.MatrixCoordinate;
import de.citec.tcs.alignment.sequence.Sequence;
import de.citec.tcs.alignment.sequence.Value;
import java.util.Iterator;
import java.util.Stack;
/**
* This is the soft pendant to the AlignmentPath. It does not really model a
* path in this case but stored all softmin "probabilities" which operation
* could have been taken in each alignment step.
*
* It is able to calculate the soft derivative.
*
* @author Benjamin Paassen - [email protected]
*/
public class SoftPathModel implements AlignmentDerivativeAlgorithm {
private final AlignmentSpecification specification;
private final Sequence left;
private final Sequence right;
private final double distance;
private final double beta;
private final SoftMatrixEntry[][] pathMatrix;
public SoftPathModel(AlignmentSpecification specification,
Sequence left, Sequence right,
double distance, double beta,
SoftMatrixEntry[][] pathMatrix) {
this.specification = specification;
this.left = left;
this.right = right;
this.distance = distance;
this.beta = beta;
this.pathMatrix = pathMatrix; //check validity
if (left.getNodeSpecification() != specification.getNodeSpecification()
&& !left.getNodeSpecification().equals(specification.getNodeSpecification())) {
throw new IllegalArgumentException(
"The first input sequence has an unexpected node specification!");
}
if (left.getNodeSpecification() != right.getNodeSpecification()
&& !left.getNodeSpecification().equals(right.getNodeSpecification())) {
throw new IllegalArgumentException(
"The node specifications of both input sequences to not match!");
}
if (left.getNodes().size() != pathMatrix.length - 1) {
throw new IllegalArgumentException(
"The given PathMatrix does not fit the given sequences!");
}
for (int i = 0; i < pathMatrix.length; i++) {
if (right.getNodes().size() != pathMatrix[i].length - 1) {
throw new IllegalArgumentException(
"The given PathMatrix does not fit the given sequences!");
}
}
//check validity of comparators.
for (int k = 0; k < specification.size(); k++) {
if (!(specification.getComparator(k) instanceof GapComparator)) {
throw new UnsupportedOperationException("The comparator for keyword "
+ specification.getKeyword(k) + " does not support gaps!");
}
}
}
/**
* {@inheritDoc }
*/
@Override
public Sequence getLeft() {
return left;
}
/**
* {@inheritDoc }
*/
@Override
public Sequence getRight() {
return right;
}
/**
* {@inheritDoc }
*/
@Override
public double getDistance() {
return distance;
}
/**
* Returns the AlignmentSpecification that was basis for this Alignment.
*
* @return the AlignmentSpecification that was basis for this Alignment.
*/
public AlignmentSpecification getSpecification() {
return specification;
}
/**
* The parameter defining the "softness" of the alignment. For beta towards
* infinity this alignment becomes closer to the strict alignment. For beta
* = 0 all possible alignments are equally considered and softmin returns
* the average. Please note that a low beta value might lead to a very rough
* approximation and that for higher sequence lengths beta has to be higher,
* too.
*
* @return The parameter defining the "softness" of the alignment. For beta
* towards infinity this alignment becomes closer to the strict alignment.
* For beta = 0 all possible alignments are equally considered and softmin
* returns the average. Please note that a low beta value might lead to a
* very rough approximation and that for higher sequence lengths beta has to
* be higher, too.
*/
public double getBeta() {
return beta;
}
/**
* This is the dynamic programming matrix calculated by a
* SoftAlignmentAlgorithm. It is not recommended to work directly with this
* matrix and detailed documentation is not within the scope of this API.
*
* @return the dynamic programming matrix calculated by a
* SoftAlignmentAlgorithm.
*/
public SoftMatrixEntry[][] getPathMatrix() {
return pathMatrix;
}
/**
* {@inheritDoc }
*/
@Override
public Y calculateParameterDerivative(
DerivableComparator comp, String keyword) {
return comp.transformToResult(calculateRawParameterDerivative(
comp, keyword));
}
/**
* {@inheritDoc }
*/
@Override
public double[] calculateRawParameterDerivative(
DerivableComparator comp, String keyword) {
final int P = comp.getNumberOfParameters();
final double[] derivatives = new double[P];
final int k = specification.getKeywordIndex(keyword);
if (specification.getComparator(k) != comp) {
throw new UnsupportedOperationException(
"The given comparator was not used for the given keyword!");
}
final double weight = specification.getWeighting()[k];
if (weight == 0) {
/*
* if the comparator has no weight, we return an empty array without
* doing any calculation.
*/
return derivatives;
}
//support sparsity
boolean sparse = comp instanceof SparseDerivableComparator;
final SparseDerivableComparator sparseComp;
if (sparse) {
sparseComp = (SparseDerivableComparator) comp;
} else {
sparseComp = null;
}
//pre-calculation caching
final int m = left.getNodes().size();
final int n = right.getNodes().size();
final int origK = specification.getOriginalIndex(k);
//start of calculation.
/*
* We emulate a recursion by using a stack to make it more efficient, as
* we do not have to backtrace the whole matrix but only some paths
* within. We can disregard paths with zero probability.
*/
final SparseMatrix sparseMatrix
= new SparseMatrix();
final Stack calcStack = new Stack();
//we start at the end and trace back.
MatrixCoordinate current = new MatrixCoordinate(m, n);
//the (0,0) cell is initialized with a zero vector.
sparseMatrix.set(0, 0, new double[P]);
calcStack.push(current);
while (!calcStack.empty()) {
current = calcStack.pop();
final int i = current.i;
final int j = current.j;
if (sparseMatrix.get(i, j) != null) {
continue;
}
//if we are in the first column, we can only delete.
double[] delOld = null;
double[] localDeriv = new double[P];
if (j == 0) {
//if we have to calculate this term, look if we have the necessary preconditions.
delOld = sparseMatrix.get(i - 1, j);
if (delOld == null) {
//if we don't have that yet, calculate it first.
calcStack.push(new MatrixCoordinate(i, j));
calcStack.push(new MatrixCoordinate(i - 1, j));
continue;
}
//if we have it, calculate result.
fillDeletionInformation(pathMatrix[i][j], pathMatrix[i - 1][j].softmin, i);
//calculate local derivative
calculateLocalDerivative(comp, sparseComp, sparse,
(X) left.getNodes().get(i - 1).getValue(origK), null,
OperationType.DELETION,
localDeriv, P);
//embed it in the softmin derivative term.
for (int p = 0; p < P; p++) {
localDeriv[p] = (delOld[p] + weight * localDeriv[p])
* pathMatrix[i][j].delSoftDerivative;
}
//store it in the matrix.
sparseMatrix.set(i, j, localDeriv);
continue;
}
//if we are in the first row, we can only insert.
double[] insOld = null;
if (i == 0) {
//if we have to calculate this term, look if we have the necessary preconditions.
insOld = sparseMatrix.get(i, j - 1);
if (insOld == null) {
//if we don't have that yet, calculate it first.
calcStack.push(new MatrixCoordinate(i, j));
calcStack.push(new MatrixCoordinate(i, j - 1));
continue;
}
//if we have it, calculate result.
fillInsertionInformation(pathMatrix[i][j],
pathMatrix[i][j - 1].softmin, j);
//calculate local derivative
calculateLocalDerivative(comp, sparseComp, sparse,
null, (X) right.getNodes().get(j - 1).getValue(origK),
OperationType.INSERTION,
localDeriv, P);
//embed it in the softmin derivative term.
for (int p = 0; p < P; p++) {
localDeriv[p] = (insOld[p] + weight * localDeriv[p])
* pathMatrix[i][j].insSoftDerivative;
}
//store it in the matrix
sparseMatrix.set(i, j, localDeriv);
continue;
}
//if we are inside the matrix, the calculation becomes more complex.
boolean pushedBack = false;
double[] repOld = null;
//handle deletion first, similar to above.
if (pathMatrix[i][j].delProb > 0) {
//if we are above threshold, look for preconditions.
delOld = sparseMatrix.get(i - 1, j);
if (delOld == null) {
//if we don't have that yet, calculate it first.
calcStack.push(new MatrixCoordinate(i, j));
pushedBack = true;
calcStack.push(new MatrixCoordinate(i - 1, j));
}
}
//handle insertion
if (pathMatrix[i][j].insProb > 0) {
//if we are above threshold, look for preconditions.
insOld = sparseMatrix.get(i, j - 1);
if (insOld == null) {
//if we don't have that yet, calculate it first.
if (!pushedBack) {
calcStack.push(new MatrixCoordinate(i, j));
pushedBack = true;
}
calcStack.push(new MatrixCoordinate(i, j - 1));
}
}
//handle replacement
if (pathMatrix[i][j].repProb > 0) {
//if we are above threshold, look for preconditions.
repOld = sparseMatrix.get(i - 1, j - 1);
if (repOld == null) {
//if we don't have that yet, calculate it first.
if (!pushedBack) {
calcStack.push(new MatrixCoordinate(i, j));
pushedBack = true;
}
calcStack.push(new MatrixCoordinate(i - 1, j - 1));
}
}
if (!pushedBack) {
//if we have all preconditions, calculate the result.
final X leftVal;
if (i > 0) {
leftVal = (X) left.getNodes().get(i - 1).getValue(origK);
} else {
leftVal = null;
}
final X rightVal;
if (j > 0) {
rightVal = (X) right.getNodes().get(j - 1).getValue(origK);
} else {
rightVal = null;
}
final double[] del = new double[P];
final double[] ins = new double[P];
final double[] rep = new double[P];
if (delOld != null) {
fillDeletionInformation(pathMatrix[i][j],
pathMatrix[i - 1][j].softmin, i);
//calculate local derivative
calculateLocalDerivative(comp, sparseComp, sparse,
leftVal, rightVal, OperationType.DELETION,
localDeriv, P);
//embed it in the softmin derivative term.
for (int p = 0; p < P; p++) {
del[p] = (delOld[p] + weight * localDeriv[p])
* pathMatrix[i][j].delSoftDerivative;
}
}
if (insOld != null) {
fillInsertionInformation(pathMatrix[i][j],
pathMatrix[i][j - 1].softmin, j);
//calculate local derivative
calculateLocalDerivative(comp, sparseComp, sparse,
leftVal, rightVal, OperationType.INSERTION,
localDeriv, P);
//embed it in the softmin derivative term.
for (int p = 0; p < P; p++) {
ins[p] = (insOld[p] + localDeriv[p] * weight)
* pathMatrix[i][j].insSoftDerivative;
}
}
if (repOld != null) {
fillReplacementInformation(pathMatrix[i][j],
pathMatrix[i - 1][j - 1].softmin, i, j);
//calculate local derivative
calculateLocalDerivative(comp, sparseComp, sparse,
leftVal, rightVal, OperationType.REPLACEMENT,
localDeriv, P);
//embed it in the softmin derivative term.
for (int p = 0; p < P; p++) {
rep[p] = (repOld[p] + localDeriv[p] * weight)
* pathMatrix[i][j].repSoftDerivative;
}
}
final double[] result = new double[P];
for (int p = 0; p < P; p++) {
result[p] = del[p] + ins[p] + rep[p];
}
sparseMatrix.set(i, j, result);
}
}
return sparseMatrix.get(m, n);
}
private static void calculateLocalDerivative(
DerivableComparator comp,
SparseDerivableComparator sparseComp, boolean sparse,
X leftVal, X rightVal, OperationType type,
double[] localDeriv, int P) {
//if we do not have a sparse comparator, we iterate over all parameters.
if (!sparse) {
for (int p = 0; p < P; p++) {
localDeriv[p] = comp.calculateLocalDerivative(p, leftVal, rightVal, type);
}
} else {
//clean local derivative.
for (int p = 0; p < P; p++) {
localDeriv[p] = 0;
}
//otherwise use sparsity
final Iterator it = sparseComp.
calculateSparseLocalDerivative(leftVal, rightVal,
type).iterator();
while (it.hasNext()) {
final SparseLocalDerivative.SparseDeriativeEntry sparseLocalDerivative = it.next();
localDeriv[sparseLocalDerivative.getParameterIndex()]
= sparseLocalDerivative.getDerivative();
}
}
}
/**
* {@inheritDoc }
*/
public double[] calculateWeightDerivative() {
final int m = left.getNodes().size();
final int n = right.getNodes().size();
//pre-calculation caching
final int K = specification.getWeighting().length;
//start of calculation.
/*
* We emulate a recursion by using a stack to make it more efficient, as
* we do not have to backtrace the whole matrix but only some paths
* within. We can disregard paths with zero probability.
*/
final SparseMatrix sparseMatrix
= new SparseMatrix();
final Stack calcStack = new Stack();
//we start at the end and trace back.
MatrixCoordinate current = new MatrixCoordinate(m, n);
calcStack.push(current);
while (!calcStack.empty()) {
current = calcStack.pop();
//get dependent cells.
final int i = current.i;
final int j = current.j;
if (sparseMatrix.get(i, j) != null) {
continue;
}
if (i == 0 && j == 0) {
//if we are at the start, store 0.
sparseMatrix.set(i, j, new WeightDerivativeEntry(0, 0, new double[K]));
continue;
}
//if we are in the first column, we can only delete.
WeightDerivativeEntry delOld = null;
if (j == 0) {
//if we have to calculate this term, look if we have the necessary preconditions.
delOld = sparseMatrix.get(i - 1, j);
if (delOld == null) {
//if we don't have that yet, calculate it first.
calcStack.push(new MatrixCoordinate(i, j));
calcStack.push(new MatrixCoordinate(i - 1, j));
continue;
}
//if we have it, calculate result.
fillDeletionInformation(pathMatrix[i][j],
pathMatrix[i - 1][j].softmin, i);
final WeightDerivativeEntry newEntry = new WeightDerivativeEntry(
i, j, new double[K]);
for (int k = 0; k < K; k++) {
newEntry.weightDerivatives[k] = (delOld.weightDerivatives[k]
+ pathMatrix[i][j].delCosts[k])
* pathMatrix[i][j].delSoftDerivative;
}
sparseMatrix.set(i, j, newEntry);
continue;
}
//if we are in the first row, we can only insert.
WeightDerivativeEntry insOld = null;
if (i == 0) {
//if we have to calculate this term, look if we have the necessary preconditions.
insOld = sparseMatrix.get(i, j - 1);
if (insOld == null) {
//if we don't have that yet, calculate it first.
calcStack.push(new MatrixCoordinate(i, j));
calcStack.push(new MatrixCoordinate(i, j - 1));
continue;
}
//if we have it, calculate result.
fillInsertionInformation(pathMatrix[i][j],
pathMatrix[i][j - 1].softmin, j);
final WeightDerivativeEntry newEntry = new WeightDerivativeEntry(
i, j, new double[K]);
for (int k = 0; k < K; k++) {
newEntry.weightDerivatives[k] = (insOld.weightDerivatives[k]
+ pathMatrix[i][j].insCosts[k])
* pathMatrix[i][j].insSoftDerivative;
}
sparseMatrix.set(i, j, newEntry);
continue;
}
//if we are inside the matrix, the calculation becomes more complex.
boolean pushedBack = false;
WeightDerivativeEntry repOld = null;
//handle deletion first, similar to above.
if (pathMatrix[i][j].delProb > 0) {
//if we are above threshold, look for preconditions.
delOld = sparseMatrix.get(i - 1, j);
if (delOld == null) {
//if we don't have that yet, calculate it first.
calcStack.push(new MatrixCoordinate(i, j));
pushedBack = true;
calcStack.push(new MatrixCoordinate(i - 1, j));
}
}
//handle insertion
if (pathMatrix[i][j].insProb > 0) {
//if we are above threshold, look for preconditions.
insOld = sparseMatrix.get(i, j - 1);
if (insOld == null) {
//if we don't have that yet, calculate it first.
if (!pushedBack) {
calcStack.push(new MatrixCoordinate(i, j));
pushedBack = true;
}
calcStack.push(new MatrixCoordinate(i, j - 1));
}
}
//handle replacement
if (pathMatrix[i][j].repProb > 0) {
//if we are above threshold, look for preconditions.
repOld = sparseMatrix.get(i - 1, j - 1);
if (repOld == null) {
//if we don't have that yet, calculate it first.
if (!pushedBack) {
calcStack.push(new MatrixCoordinate(i, j));
pushedBack = true;
}
calcStack.push(new MatrixCoordinate(i - 1, j - 1));
}
}
if (!pushedBack) {
//if we have all preconditions, calculate the result.
final double[] delVals = new double[K];
final double[] insVals = new double[K];
final double[] repVals = new double[K];
if (delOld != null) {
fillDeletionInformation(pathMatrix[i][j],
pathMatrix[i - 1][j].softmin, i);
for (int k = 0; k < K; k++) {
delVals[k] = (delOld.weightDerivatives[k]
+ pathMatrix[i][j].delCosts[k])
* pathMatrix[i][j].delSoftDerivative;
}
}
if (insOld != null) {
fillInsertionInformation(pathMatrix[i][j],
pathMatrix[i][j - 1].softmin, j);
for (int k = 0; k < K; k++) {
insVals[k] = (insOld.weightDerivatives[k]
+ pathMatrix[i][j].insCosts[k])
* pathMatrix[i][j].insSoftDerivative;
}
}
if (repOld != null) {
fillReplacementInformation(pathMatrix[i][j],
pathMatrix[i - 1][j - 1].softmin, i, j);
for (int k = 0; k < K; k++) {
repVals[k] = (repOld.weightDerivatives[k]
+ pathMatrix[i][j].repCosts[k])
* pathMatrix[i][j].repSoftDerivative;
}
}
final double[] weightDerivs = new double[K];
for (int k = 0; k < K; k++) {
weightDerivs[k] = delVals[k] + insVals[k] + repVals[k];
}
final WeightDerivativeEntry newEntry
= new WeightDerivativeEntry(i, j, weightDerivs);
sparseMatrix.set(i, j, newEntry);
}
}
return sparseMatrix.get(m, n).weightDerivatives;
}
/**
* Fills the missing information for the given SoftMatrixEntry. We calculate
* this here on demand to prevent unnecessary calculations.
*
* Please note that this method is hand-tailored for the combination of
* SoftAlignmentFullAlgorithm and SoftPathModel. It is not recommended to
* use this for other purposes.
*
* @param entry
* @param i
*/
private void fillDeletionInformation(SoftMatrixEntry entry,
double delOldScore, int i) {
if (entry.delCosts == null) {
entry.delCosts = specification.calculateDeletionCosts(
left.getNodes().get(i - 1));
entry.delSoftDerivative = entry.delProb * (1 - beta * (delOldScore
+ entry.delLocal - entry.softmin));
}
}
/**
* Fills the missing information for the given SoftMatrixEntry. We calculate
* this here on demand to prevent unnecessary calculations.
*
* Please note that this method is hand-tailored for the combination of
* SoftAlignmentFullAlgorithm and SoftPathModel. It is not recommended to
* use this for other purposes.
*
* @param entry
* @param j
*/
private void fillInsertionInformation(SoftMatrixEntry entry,
double insOldScore, int j) {
if (entry.insCosts == null) {
entry.insCosts = specification.calculateInsertionCosts(
right.getNodes().get(j - 1));
entry.insSoftDerivative = entry.insProb * (1 - beta * (insOldScore
+ entry.insLocal - entry.softmin));
}
}
/**
* Fills the missing information for the given SoftMatrixEntry. We calculate
* this here on demand to prevent unnecessary calculations.
*
* Please note that this method is hand-tailored for the combination of
* SoftAlignmentFullAlgorithm and SoftPathModel. It is not recommended to
* use this for other purposes.
*
* @param entry
* @param i
* @param j
*/
private void fillReplacementInformation(SoftMatrixEntry entry,
double repOldScore, int i, int j) {
if (entry.repCosts == null) {
entry.repCosts = specification.calculateReplacementCosts(
left.getNodes().get(i - 1),
right.getNodes().get(j - 1));
entry.repSoftDerivative = entry.repProb * (1 - beta * (repOldScore
+ entry.repLocal - entry.softmin));
}
}
public static class SoftMatrixEntry implements AlignmentMatrixEntry {
private double[] delCosts;
private double delLocal;
private double delProb;
private double delSoftDerivative;
private double[] insCosts;
private double insLocal;
private double insProb;
private double insSoftDerivative;
private double[] repCosts;
private double repLocal;
private double repProb;
private double repSoftDerivative;
private double softmin;
public SoftMatrixEntry() {
}
public double[] getDelCosts() {
return delCosts;
}
public void setDelCosts(double[] delCosts) {
this.delCosts = delCosts;
}
public double getDelLocal() {
return delLocal;
}
public void setDelLocal(double delLocal) {
this.delLocal = delLocal;
}
public double getDelProb() {
return delProb;
}
public void setDelProb(double delProb) {
this.delProb = delProb;
}
public double getDelSoftDerivative() {
return delSoftDerivative;
}
public void setDelSoftDerivative(double delSoftDerivative) {
this.delSoftDerivative = delSoftDerivative;
}
public double[] getInsCosts() {
return insCosts;
}
public void setInsCosts(double[] insCosts) {
this.insCosts = insCosts;
}
public double getInsLocal() {
return insLocal;
}
public void setInsLocal(double insLocal) {
this.insLocal = insLocal;
}
public double getInsProb() {
return insProb;
}
public void setInsProb(double insProb) {
this.insProb = insProb;
}
public double getInsSoftDerivative() {
return insSoftDerivative;
}
public void setInsSoftDerivative(double insSoftDerivative) {
this.insSoftDerivative = insSoftDerivative;
}
public double[] getRepCosts() {
return repCosts;
}
public void setRepCosts(double[] repCosts) {
this.repCosts = repCosts;
}
public double getRepLocal() {
return repLocal;
}
public void setRepLocal(double repLocal) {
this.repLocal = repLocal;
}
public double getRepProb() {
return repProb;
}
public void setRepProb(double repProb) {
this.repProb = repProb;
}
public double getRepSoftDerivative() {
return repSoftDerivative;
}
public void setRepSoftDerivative(double repSoftDerivative) {
this.repSoftDerivative = repSoftDerivative;
}
public double getSoftmin() {
return softmin;
}
public void setSoftmin(double softmin) {
this.softmin = softmin;
}
}
private static class WeightDerivativeEntry {
public int i;
public int j;
public double[] weightDerivatives;
public WeightDerivativeEntry() {
}
public WeightDerivativeEntry(int i, int j, double[] weightDerivatives) {
this.i = i;
this.j = j;
this.weightDerivatives = weightDerivatives;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy