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

de.citec.tcs.alignment.Operation 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.GapComparator;
import de.citec.tcs.alignment.comparators.OperationType;
import de.citec.tcs.alignment.comparators.SkipComparator;
import de.citec.tcs.alignment.sequence.Node;
import java.util.Arrays;

/**
 * This defines an applied alignment operation. It is specified by the nodes
 * that have been aligned from the left or the right sequence in the alignment.
 * If a node was replaced by a gap it is denoted here as a null reference.
 *
 * @author Benjamin Paassen - [email protected]
 */
public class Operation {

	private final Node left;
	private final Node right;
	private final OperationType type;
	private final AlignmentPath path;
	private final double[] comparatorDistances;

	/**
	 * This constructs an Operation within an AlignmentPath.
	 *
	 * @param left The Node from the left input sequence that was used here or
	 * null if this is a DELETION/SKIPDELETION operation.
	 * @param right The Node from the right input sequence that was used here or
	 * null if this is an INSERTION/SKIPINSERTION operation.
	 * @param type The type of this Operation. Please refer to the OperationType
	 * class for more information.
	 * @param path The AlignmentPath this Operation belongs to.
	 */
	public Operation(Node left, Node right, OperationType type, AlignmentPath path) {
		this.left = left;
		this.right = right;
		this.type = type;
		this.path = path;
		this.comparatorDistances = new double[path.getSpecificaton().size()];
		//check validity.
		switch (type) {
			case DELETION:
			case SKIPDELETION:
				if (right != null) {
					throw new RuntimeException("Expected null for a gap/skip but was not null!");
				}
				break;
			case INSERTION:
			case SKIPINSERTION:
				if (left != null) {
					throw new RuntimeException("Expected null for a gap/skip but was not null!");
				}
				break;
		}

		switch (type) {
			case DELETION:
			case SKIPDELETION:
			case DELETIONREPLACEMENT:
			case INSERTIONREPLACEMENT:
			case REPLACEMENT:
				if (left.getSequence() != path.getLeft()) {
					throw new RuntimeException(
							"The given left node does not belong to the left sequence of this path!");
				}
		}
		switch (type) {
			case INSERTION:
			case SKIPINSERTION:
			case INSERTIONREPLACEMENT:
			case DELETIONREPLACEMENT:
			case REPLACEMENT:
				if (right.getSequence() != path.getRight()) {
					throw new RuntimeException(
							"The given right node does not belong to the right sequence of this path!");
				}
		}
	}

	/**
	 *
	 * @return The Node from the left input sequence that was used here or null
	 * if this is a DELETION/SKIPDELETION operation.
	 */
	public Node getLeft() {
		return left;
	}

	/**
	 *
	 * @return The Node from the right input sequence that was used here or null
	 * if this is an INSERTION/SKIPINSERTION operation.
	 */
	public Node getRight() {
		return right;
	}

	/**
	 *
	 * @return The AlignmentPath this Operation belongs to.
	 */
	public AlignmentPath getPath() {
		return path;
	}

	/**
	 *
	 * @return The type of this Operation. Please refer to the OperationType
	 * class for more information.
	 */
	public OperationType getType() {
		return type;
	}

	/**
	 * Returns the local costs given by the Comparators specified for this
	 * Alignment to align the left Node in this Operation with the right Node
	 * (or deleting, inserting, skipping nodes respectively).
	 *
	 * Please refer to the corresponding setter method for more information.
	 *
	 * @return The local costs given by the Comparators specified for this
	 * Alignment to align the left Node in this Operation with the right Node
	 * (or deleting, inserting, skipping nodes respectively).
	 */
	public double[] getComparatorDistances() {
		return comparatorDistances;
	}

	/**
	 *
	 * @return The vector product of the keyword weighting specified in the
	 * AlignmentSpecification and the ComparatorDistances.
	 */
	public double getWeightedLocalCost() {
		double sum = 0;
		final double[] weighting = path.getSpecificaton().getWeighting();
		for (int i = 0; i < comparatorDistances.length; i++) {
			if (weighting[i] > 0) {
				sum += weighting[i] * comparatorDistances[i];
			}
		}
		return sum;
	}

	/**
	 * Sets the local costs given by the Comparators specified for this
	 * Alignment to align the left Node in this Operation with the right Node
	 * (or deleting, inserting, skipping nodes respectively).
	 *
	 * Say the comparators are functions
	 *
	 * c_k : V_k x V_k to Real Numbers
	 *
	 * where V_k is the space of possible Values for the keyword k. Say that a_k
	 * and b_k are the values for the keyword k in the left node (a) and the
	 * right node (b).
	 *
	 * Than the return value here is the vector c_k(a_k,b_k) for all k.
	 *
	 * @param distances The local costs given by the Comparators specified for
	 * this Alignment to align the left Node in this Operation with the right
	 * Node (or deleting, inserting, skipping nodes respectively).
	 */
	public void setComparatorDistances(double[] distances) {
		if (comparatorDistances.length != distances.length) {
			throw new RuntimeException("The wrong number of distances was given!");
		}
		System.arraycopy(distances, 0, comparatorDistances, 0, distances.length);
	}

	public static double[] calculateComparatorDistances(OperationType op,
			Node left, Node right, AlignmentSpecification spec) {
		double[] costs = new double[spec.size()];
		for (int k = 0; k < costs.length; k++) {
			switch (op) {
				case REPLACEMENT:
				case DELETIONREPLACEMENT:
				case INSERTIONREPLACEMENT:
					costs[k] = spec.getComparator(k).compare(
							left.getValue(spec.getOriginalIndex(k)),
							right.getValue(spec.getOriginalIndex(k)));
					break;
				case DELETION: {
					final GapComparator comp = (GapComparator) spec.getComparator(k);
					costs[k] = comp.delete(left.getValue(spec.getOriginalIndex(k)));
					break;
				}
				case INSERTION: {
					final GapComparator comp = (GapComparator) spec.getComparator(k);
					costs[k] = comp.insert(right.getValue(spec.getOriginalIndex(k)));
					break;
				}
				case SKIPDELETION: {
					final SkipComparator comp = (SkipComparator) spec.getComparator(k);
					costs[k] = comp.skipDelete(left.getValue(spec.getOriginalIndex(k)));
					break;
				}
				case SKIPINSERTION: {
					final SkipComparator comp = (SkipComparator) spec.getComparator(k);
					costs[k] = comp.skipInsert(right.getValue(spec.getOriginalIndex(k)));
					break;
				}
				default:
					throw new UnsupportedOperationException("Unknown operation: " + op);
			}
		}
		return costs;
	}

	/**
	 * {@inheritDoc }
	 */
	@Override
	public String toString() {
		final StringBuilder builder = new StringBuilder();
		final int K = path.getSpecificaton().size();
		if (K > 1) {
			builder.append(type);
			builder.append("\nWeighted Local Cost = ");
			builder.append(getWeightedLocalCost());
			builder.append("\n");
		}
		for (int k = 0; k < K; k++) {
			if (K > 1) {
				builder.append(path.getSpecificaton().getKeyword(k));
				builder.append(" : ");
			}
			if (left != null) {
				builder.append(left.getValue(path.getSpecificaton().getOriginalIndex(k)));
			} else {
				switch (type) {
					case INSERTION:
						builder.append("-");
						break;
					case SKIPINSERTION:
						builder.append("_");
						break;
				}
			}
			builder.append(" vs. ");
			if (right != null) {
				builder.append(right.getValue(path.getSpecificaton().getOriginalIndex(k)));
			} else {
				switch (type) {
					case DELETION:
						builder.append("-");
						break;
					case SKIPDELETION:
						builder.append("_");
						break;
				}
			}
			builder.append(" = ");
			builder.append(comparatorDistances[k]);
			if (K > 1) {
				builder.append("\n");
			}
		}
		if (K > 1) {
			builder.delete(builder.length() - 1, builder.length());
		}
		return builder.toString();
	}

	/**
	 * {@inheritDoc }
	 */
	@Override
	public int hashCode() {
		int hash = 7;
		hash = 53 * hash + (this.left != null ? this.left.hashCode() : 0);
		hash = 53 * hash + (this.right != null ? this.right.hashCode() : 0);
		hash = 53 * hash + (this.type != null ? this.type.hashCode() : 0);
		hash = 53 * hash + Arrays.hashCode(this.comparatorDistances);
		return hash;
	}

	/**
	 * {@inheritDoc }
	 */
	@Override
	public boolean equals(Object obj) {
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		final Operation other = (Operation) obj;
		if (this.left != other.left && (this.left == null || !this.left.equals(other.left))) {
			return false;
		}
		if (this.right != other.right && (this.right == null || !this.right.equals(other.right))) {
			return false;
		}
		if (this.type != other.type) {
			return false;
		}
		if (!Arrays.equals(this.comparatorDistances, other.comparatorDistances)) {
			return false;
		}
		return true;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy