
de.citec.tcs.alignment.SoftDTWModel 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.Comparator;
import de.citec.tcs.alignment.comparators.OperationType;
import de.citec.tcs.alignment.sequence.Sequence;
import de.citec.tcs.alignment.sequence.Value;
import de.citec.tcs.alignment.sequence.Node;
import java.util.Arrays;
/**
* storage unit for soft DTW alignment result, provides functions to calculate
* the soft DTW derivatives based on the stored soft alignment
*
* @author Georg Zentgraf
*/
public class SoftDTWModel implements AlignmentDerivativeAlgorithm {
private final AlignmentSpecification specificaton;
private final Sequence x;
private final Sequence y;
private final double[][] similarityMatrix;
public SoftDTWModel(AlignmentSpecification specificaton, Sequence x,
Sequence y, double[][] similarityMatrix) {
this.specificaton = specificaton;
this.x = x;
this.y = y;
this.similarityMatrix = similarityMatrix;
//check validity
if (x.getNodeSpecification() != specificaton.getNodeSpecification()
&& !x.getNodeSpecification().equals(specificaton.getNodeSpecification())) {
throw new RuntimeException(
"The first input sequence has an unexpected node specification!");
}
if (x.getNodeSpecification() != y.getNodeSpecification()
&& !x.getNodeSpecification().equals(y.getNodeSpecification())) {
throw new RuntimeException(
"The node specifications of both input sequences to not match!");
}
if (x.getNodes().size() != similarityMatrix.length - 1
|| y.getNodes().size() != similarityMatrix[0].length - 1) {
throw new RuntimeException("The given similarityMatrix does not fit the given sequences!");
}
}
/**
* Returns the AlignmentSpecification that was basis for this Alignment.
*
* @return the AlignmentSpecification that was basis for this Alignment.
*/
public AlignmentSpecification getSpecificaton() {
return specificaton;
}
/**
* This is the dynamic programming matrix calculated by a
* DynamicTimeWarpingAlgorithm. 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
* DynamicTimeWarpingAlgorithm.
*/
public double[][] getSimilarityMatrix() {
return similarityMatrix;
}
/**
* Returns the softDTW score between the two input sequences. The score is
* not-normalized a similarity measure: 0 = perfect dissimilarity maxVal =
* perfect similarity (determined by length of sequences)
*
* @return softDTW score between the two input sequences.
*/
public double getSimilarityScore() {
return this.similarityMatrix[this.similarityMatrix.length - 1][this.similarityMatrix[0].length - 1];
}
/**
* {@inheritDoc }
*/
@Override
public Y calculateParameterDerivative(
DerivableComparator comp, String keyword) {
return comp.transformToResult(calculateRawParameterDerivative(comp, keyword));
}
/**
* {@inheritDoc }
*
* NOTE: for this implementation of calculateDerivative comp has to also
* implement Comparator
*/
public double[] calculateRawParameterDerivative(
DerivableComparator comp, String keyword) {
// pre-calculation caching:
final int P = comp.getNumberOfParameters();
final double[] derivatives = new double[P];
// number of comparators
int compCount = this.specificaton.size();
// get comparators, weights and original indezes (used for accessing NodeSpec and Sequence)
final double[] weights = this.specificaton.getWeighting();
final Comparator[] comparators = new Comparator[compCount];
final int[] originalIndices = new int[compCount];
for (int k = 0; k < compCount; k++) {
comparators[k] = (Comparator) this.specificaton.getComparator(k);
originalIndices[k] = this.specificaton.getOriginalIndex(k);
}
final int compIndex = this.specificaton.getKeywordIndex(keyword);
if (specificaton.getComparator(compIndex) != comp) {
throw new RuntimeException("The given comparator was not used for the given keyword!");
}
// (weight = 0) == (derivation = 0)
if (weights[compIndex] == 0) {
return derivatives;
}
final int n = this.x.getNodes().size();
final int m = this.y.getNodes().size();
Node nodeX, nodeY;
for (int p = 0; p < P; p++) {
// start of calculation
double[][] derivativeMatrix = new double[n + 1][m + 1];
// again use indezes 1-N, 1-M of derivativeMatrix and initialize
// 0.th row and 0th. column with [0,0,0,0..] for use of simplified
// dynamic programming scheme
// initialize first entry.
derivativeMatrix[0][0] = 0;
// initialize first row and column
for (int j = 1; j <= m; j++) {
derivativeMatrix[0][j] = 0;
}
for (int i = 1; i <= n; i++) {
derivativeMatrix[i][0] = 0;
}
// calculate derivativeMatrix via dynamic programming
double weightedCompSum, localDeriv;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
// get current nodes
nodeX = this.x.getNodes().get(i - 1);
nodeY = this.y.getNodes().get(j - 1);
// calculate weighted comparator sum
weightedCompSum = SoftDTWModel.calc_weighted_comp_sum(comparators, weights, originalIndices, nodeX, nodeY);
// calc kernel derivation dKappa/dLambda
X valueX = (X) nodeX.getValue(originalIndices[compIndex]);
X valueY = (X) nodeY.getValue(originalIndices[compIndex]);
double xx = comp.calculateLocalDerivative(p, valueX, valueY, OperationType.REPLACEMENT);
localDeriv = 1 / ((1 - weightedCompSum) * (1 - weightedCompSum)) * weights[compIndex]
* comp.calculateLocalDerivative(p, valueX, valueY, OperationType.REPLACEMENT);
// calculate derivation
derivativeMatrix[i][j] = localDeriv * (this.similarityMatrix[i - 1][j]
+ this.similarityMatrix[i][j - 1]
+ this.similarityMatrix[i - 1][j - 1])
+ weightedCompSum / (1 - weightedCompSum)
* (derivativeMatrix[i - 1][j]
+ derivativeMatrix[i][j - 1]
+ derivativeMatrix[i - 1][j - 1]);
}
}
derivatives[p] = derivativeMatrix[n][m];
}
return derivatives;
}
/**
* {@inheritDoc }
*/
public double[] calculateWeightDerivative() {
// pre-calculation caching:
// get comparators, weights and original indezes (used for accessing NodeSpec and Sequence)
final double[] weights = this.specificaton.getWeighting();
final int weightCount = weights.length;
final Comparator[] comparators = new Comparator[weightCount];
final int[] originalIndices = new int[weightCount];
for (int k = 0; k < weightCount; k++) {
comparators[k] = (Comparator) this.specificaton.getComparator(k);
originalIndices[k] = this.specificaton.getOriginalIndex(k);
}
final int n = this.x.getNodes().size();
final int m = this.y.getNodes().size();
Node nodeX, nodeY;
// start of calculation
WeightDerivativeEntry[][] derivativeMatrix = new WeightDerivativeEntry[n + 1][m + 1];
// again use indezes 1-N, 1-M of derivativeMatrix and initialize
// 0.th row and 0th. column with [1,0,0,0..] for use of simplified
// dynamic programming scheme
// initialize first entry.
double[] matInit = new double[weightCount];
for (int i = 0; i < weightCount; i++) {
matInit[i] = 1;
}
derivativeMatrix[0][0] = new WeightDerivativeEntry(Arrays.copyOf(matInit, weightCount));
// initialize first row and column
for (int i = 0; i < weightCount; i++) {
matInit[i] = 0;
}
for (int j = 1; j <= m; j++) {
derivativeMatrix[0][j] = new WeightDerivativeEntry(Arrays.copyOf(matInit, weightCount));
}
for (int i = 1; i <= n; i++) {
derivativeMatrix[i][0] = new WeightDerivativeEntry(Arrays.copyOf(matInit, weightCount));
}
// calculate derivativeMatrix via dynamic programming
double weightedCompSum, localDeriv;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
// get current nodes
nodeX = this.x.getNodes().get(i - 1);
nodeY = this.y.getNodes().get(j - 1);
// calculate weighted comparator sum
weightedCompSum = SoftDTWModel.calc_weighted_comp_sum(comparators, weights, originalIndices, nodeX, nodeY);
// calculate derivative for every weight
for (int k = 0; k < weightCount; k++) {
// calc kernel derivation dKappa/dGamma
Value valueX = nodeX.getValue(k);
Value valueY = nodeY.getValue(k);
localDeriv = comparators[k].compare(valueX, valueY)
/ ((1 - weightedCompSum) * (1 - weightedCompSum));
// calculate weight derivative
matInit[k] = localDeriv * (this.similarityMatrix[i - 1][j]
+ this.similarityMatrix[i][j - 1]
+ this.similarityMatrix[i - 1][j - 1])
+ weightedCompSum / (1 - weightedCompSum)
* (derivativeMatrix[i - 1][j].weightDerivatives[k]
+ derivativeMatrix[i][j - 1].weightDerivatives[k]
+ derivativeMatrix[i - 1][j - 1].weightDerivatives[k]);
}
// update derivativeMatrix
derivativeMatrix[i][j] = new WeightDerivativeEntry(Arrays.copyOf(matInit, weightCount));
}
}
return derivativeMatrix[n][m].weightDerivatives;
}
//-----------------------------------------------------------------------------
/**
* Returns weighted sum k of local comparators: k = sum(w_i * k_i) with:
* k_i: local comparators w_i: local weights
*
* Note: Comparators, weights, originalIndices are supposed to originate
* from the same AlignmentSpecification. They are the same for every pair of
* nodes and can be 'precalculated'.
*
* @param comparators local comparators k_i (from AlignmentSpecification)
* @param weights local weights w_i (from AlignmentSpecification)
* @param originalIndices indices (corresponding to comparators and weights)
* to get correct node values (from AlignmentSpecification)
* @param nodeX first node to compare
* @param nodeY second node to compare
*
* @return weighted sum of local comparators
*/
public static double calc_weighted_comp_sum(Comparator[] comparators, double[] weights,
int[] originalIndices, Node nodeX, Node nodeY) {
int compCount = comparators.length;
// get keyword values
final Value[] valuesX = new Value[compCount];
final Value[] valuesY = new Value[compCount];
for (int k = 0; k < compCount; k++) {
valuesX[k] = nodeX.getValue(originalIndices[k]);
valuesY[k] = nodeY.getValue(originalIndices[k]);
}
// calculate local kernel value
double kernel = 0;
for (int k = 0; k < compCount; k++) {
kernel += weights[k] * comparators[k].compare(valuesX[k], valuesY[k]);
}
return kernel;
}
/**
* {@inheritDoc }
*/
@Override
public double getDistance() {
return similarityMatrix[x.getNodes().size()][y.getNodes().size()];
}
/**
* {@inheritDoc }
*/
@Override
public Sequence getLeft() {
return x;
}
/**
* {@inheritDoc }
*/
@Override
public Sequence getRight() {
return y;
}
// store vector of weight derivatives
private static class WeightDerivativeEntry {
public double[] weightDerivatives;
public WeightDerivativeEntry() {
}
public WeightDerivativeEntry(double[] weightDerivatives) {
this.weightDerivatives = weightDerivatives;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy