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

eu.interedition.collatex.medite.AlignmentDecisionGraph Maven / Gradle / Ivy

Go to download

A Java library for collating textual sources, for example, to produce an apparatus.

There is a newer version: 1.7.1
Show newest version
/*
 * Copyright (c) 2015 The Interedition Development Group.
 *
 * This file is part of CollateX.
 *
 * CollateX 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.
 *
 * CollateX 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 CollateX.  If not, see .
 */

package eu.interedition.collatex.medite;

import eu.interedition.collatex.util.VertexMatch;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.PriorityQueue;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Function;

/**
 * @author Gregor Middell
 */
public class AlignmentDecisionGraph {

    private final List> matches;
    private final Function, Integer> matchEvaluator;
    private final PriorityQueue bestPaths;
    private final Map minCosts;

    AlignmentDecisionGraph(List> matches, Function, Integer> matchEvaluator) {
        this.matches = matches;
        this.matchEvaluator = matchEvaluator;
        this.bestPaths = new PriorityQueue<>(matches.size(), Comparator.comparingInt(n -> n.cost));
        this.minCosts = new HashMap<>();
    }

    static SortedSet> filter(SortedSet> matches, Function, Integer> matchEvaluator) {
        final SortedSet> alignments = new TreeSet<>(VertexMatch.setComparator());

        final List> matchList = new ArrayList<>(matches);
        Node optimal = new AlignmentDecisionGraph(matchList, matchEvaluator).findBestPath();
        while (optimal.matchIndex >= 0) {
            if (optimal.aligned) {
                alignments.add(matchList.get(optimal.matchIndex));
            }
            optimal = optimal.previous;
        }
        return alignments;
    }

    private Node findBestPath() {
        bestPaths.add(new Node(-1, false));
        while (!bestPaths.isEmpty()) {
            final Node current = bestPaths.remove();
            if (current.matchIndex == matches.size() - 1) {
                return current;
            }
            for (Node successor : current.successors()) {
                final int tentativeCost = cost(current) + cost(successor);
                if (bestPaths.contains(successor) && tentativeCost >= minCosts.get(successor)) {
                    continue;
                }
                minCosts.put(successor, tentativeCost);

                successor.cost = tentativeCost + heuristicCost(successor);
                successor.previous = current;
                bestPaths.remove(successor);
                bestPaths.add(successor);
            }
        }
        throw new IllegalStateException("No optimal alignment found");
    }

    private int heuristicCost(Node path) {
        final SortedSet evaluated = matches.get(path.matchIndex);
        final VertexMatch.WithTokenIndex lastMatch = evaluated.last();

        int cost = 0;
        for (SortedSet following : matches.subList(path.matchIndex + 1, matches.size())) {
            final VertexMatch.WithTokenIndex followingFirstMatch = following.first();
            if (lastMatch.vertexRank < followingFirstMatch.vertexRank && lastMatch.token < followingFirstMatch.token) {
                // we still can align this following match as the matched components are to the right of this path's last match
                continue;
            }
            // we cannot align this following match, so add it to the cost
            cost += value(following);
        }
        return cost;
    }

    private int cost(Node current) {
        int cost = 0;
        while (current != null && current.matchIndex >= 0) {
            if (!current.aligned) {
                cost += value(matches.get(current.matchIndex));
            }
            current = current.previous;
        }
        return cost;
    }

    private int value(SortedSet match) {
        return matchEvaluator.apply(match);
    }

    static class Node {
        final int matchIndex;
        final boolean aligned;
        Node previous;
        int cost;

        Node(int matchIndex, boolean aligned) {
            this.matchIndex = matchIndex;
            this.aligned = aligned;
        }

        Node[] successors() {
            final int nextIndex = matchIndex + 1;
            return new Node[]{new Node(nextIndex, true), new Node(nextIndex, false)};
        }

        @Override
        public boolean equals(Object obj) {
            if (obj != null && obj instanceof Node) {
                final Node other = (Node) obj;
                return (matchIndex == other.matchIndex) && (aligned == other.aligned);
            }
            return super.equals(obj);
        }

        @Override
        public int hashCode() {
            return Objects.hash(matchIndex, aligned);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy