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

pl.poznan.put.structure.pseudoknots.ConflictGraph Maven / Gradle / Ivy

package pl.poznan.put.structure.pseudoknots;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.math3.util.CombinatoricsUtils;
import org.immutables.value.Value;
import pl.poznan.put.structure.pseudoknots.dp.ConflictClique;
import pl.poznan.put.structure.pseudoknots.dp.ImmutableConflictClique;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * A graph of conflicts between regions. A conflict is when one region starts/end in the middle of
 * another region.d
 */
@Value.Immutable
public abstract class ConflictGraph {
  /**
   * Checks if given regions are conflicting. A conflict is when one region starts/ends in the
   * middle of another region.
   *
   * @param first The first region.
   * @param second The second region.
   * @return True if regions are conflicting.
   */
  public static boolean isConflicting(final Region first, final Region second) {
    final int firstBegin = first.begin();
    final int firstEnd = first.end();
    final int secondBegin = second.begin();
    final int secondEnd = second.end();

    if (firstBegin < secondBegin) {
      if (firstEnd < secondEnd) {
        if (secondBegin < firstEnd) {
          return true;
        }
      }
    }
    if (secondBegin < firstBegin) {
      if (secondEnd < firstEnd) {
        return firstBegin < secondEnd;
      }
    }
    return false;
  }

  @Value.Parameter(order = 1)
  protected abstract List regions();

  /**
   * Removes region and all associated conflicts with it from the graph.
   *
   * @param region The region to remove.
   */
  public final void removeRegion(final Region region) {
    if (conflicts().containsKey(region)) {
      for (final Region conflicted : conflicts().get(region)) {
        conflicts().get(conflicted).remove(region);
        if (conflicts().get(conflicted).isEmpty()) {
          conflicts().remove(conflicted);
        }
      }
      conflicts().remove(region);
    }
  }

  /**
   * Returns all regions that conflict with a given one.
   *
   * @param region The region to search conflict for.
   * @return A collection of regions in conflict with the given one.
   */
  public final Set conflictsWith(final Region region) {
    return conflicts().containsKey(region)
        ? Collections.unmodifiableSet(conflicts().get(region))
        : Collections.emptySet();
  }

  /**
   * Returns all regions that are in conflict with at least one other region.
   *
   * @return A set of regions which are in at least one conflict.
   */
  public final Set regionsWithConflicts() {
    return Collections.unmodifiableSet(conflicts().keySet());
  }

  /** @return True if at least one region has at least one conflict. */
  public final boolean hasConflicts() {
    return !conflicts().isEmpty();
  }

  /**
   * Checks if a given region has any conflicts.
   *
   * @param region The region to check.
   * @return True if the given region has at least one conflict.
   */
  public final boolean hasConflicts(final Region region) {
    return conflicts().containsKey(region);
  }

  /**
   * Searches for connected components (named conflict cliques, although they are not real cliques).
   *
   * @return The list of conflict cliques.
   */
  public final List conflictCliques() {
    final List conflictCliques = new ArrayList<>();
    final Collection seen = new HashSet<>();

    for (final Region region : conflicts().keySet()) {
      if (seen.contains(region)) {
        continue;
      }

      final Collection todo = new HashSet<>();
      todo.add(region);
      final Set done = new HashSet<>();

      while (!CollectionUtils.isEqualCollection(todo, done)) {
        final Collection next = new ArrayList<>();
        for (final Region r : todo) {
          next.addAll(conflicts().get(r));
          done.add(r);
        }
        todo.addAll(next);
      }

      if (done.size() > 1) {
        conflictCliques.add(ImmutableConflictClique.of(done));
      }

      seen.addAll(done);
    }

    return conflictCliques;
  }

  /**
   * Creates a simplified copy of this instance. The simplification merges smaller region which are
   * fully embedded into the bigger ones, if both the small and big region have exactly the same
   * conflicts.
   *
   * @return A simplified instance of this conflict graph.
   */
  public final ConflictGraph simplified() {
    final Collection toRemove = new ArrayList<>();
    final Collection toAdd = new ArrayList<>();

    ConflictGraph result = ImmutableConflictGraph.copyOf(this);
    do {
      final List conflicted = new ArrayList<>(result.regionsWithConflicts());
      toRemove.clear();
      toAdd.clear();

      for (int i = 0; i < conflicted.size(); i++) {
        final Region ri = conflicted.get(i);
        final int riBegin = ri.begin();
        final int riEnd = ri.end();

        for (int j = i + 1; j < conflicted.size(); j++) {
          final Region rj = conflicted.get(j);
          final int rjBegin = rj.begin();
          final int rjEnd = rj.end();

          final boolean b1 = (riBegin <= rjBegin) && (riEnd >= rjEnd);
          final boolean b2 = (rjBegin <= riBegin) && (rjEnd >= riEnd);
          if ((b1 || b2)
              && CollectionUtils.isEqualCollection(
                  result.conflictsWith(ri), result.conflictsWith(rj))) {
            toRemove.add(ri);
            toRemove.add(rj);
            toAdd.add(Region.merge(ri, rj));
            break;
          }
        }

        if (!toRemove.isEmpty()) {
          break;
        }
      }

      if (!toRemove.isEmpty()) {
        final List regionsCopy = new ArrayList<>(regions());
        regionsCopy.removeAll(toRemove);
        regionsCopy.addAll(toAdd);
        result = ImmutableConflictGraph.of(regionsCopy);
      }
    } while (!toRemove.isEmpty());

    return result;
  }

  @Value.Lazy
  protected Map> conflicts() {
    if (regions().size() < 2) {
      return Collections.emptyMap();
    }

    final Map> map = new HashMap<>();

    final Iterator iterator = CombinatoricsUtils.combinationsIterator(regions().size(), 2);
    final Iterable iterable = () -> iterator;
    StreamSupport.stream(iterable.spliterator(), false)
        .map(ints -> Pair.of(regions().get(ints[0]), regions().get(ints[1])))
        .filter(pair -> ConflictGraph.isConflicting(pair.getLeft(), pair.getRight()))
        .flatMap(pair -> Stream.of(pair, Pair.of(pair.getRight(), pair.getLeft())))
        .distinct()
        .forEach(
            pair -> {
              map.putIfAbsent(pair.getLeft(), new HashSet<>());
              map.get(pair.getLeft()).add(pair.getRight());
            });

    return map;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy