All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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