/* * Copyright 2012 Odysseus Software GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.odysseus.ithaka.digraph.util.fas; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import de.odysseus.ithaka.digraph.Digraph; import de.odysseus.ithaka.digraph.DigraphFactory; import de.odysseus.ithaka.digraph.Digraphs; import de.odysseus.ithaka.digraph.EdgeWeights; import de.odysseus.ithaka.digraph.MapDigraph; /** * Simple feedback arc set provider. */ public class SimpleFeedbackArcSetProvider extends AbstractFeedbackArcSetProvider { /** * Calculate feedback arc in the current thread. */ public SimpleFeedbackArcSetProvider() { super(true); } /** * Calculate feedback arc set using the specified number of threads. */ public SimpleFeedbackArcSetProvider(int numberOfThreads) { super(numberOfThreads); } /** * create equivalent graphs whith different edge orderings. * @param digraph digraph to copy * @param weights edge weights * @return list of copies */ private <V,E> List<Digraph<V,E>> copies(Digraph<V,E> digraph, int count) { List<Digraph<V,E>> copies = new ArrayList<Digraph<V,E>>(); copies.add(digraph); final List<Integer> shuffle = new ArrayList<Integer>(); final Map<V, Integer> order = new HashMap<V, Integer>(); int index = 0; for (V source : digraph.vertices()) { order.put(source, index); shuffle.add(index++); } Random random = new Random(7); for (int i = 0; i < count; i++) { Collections.shuffle(shuffle, random); copies.add(Digraphs.copy(digraph, new DigraphFactory<Digraph<V,E>>() { List<Integer> index = new ArrayList<Integer>(shuffle); @Override public Digraph<V, E> create() { return new MapDigraph<V, E>(new Comparator<V>() { @Override public int compare(V v1, V v2) { int value1 = index.get(order.get(v1)); int value2 = index.get(order.get(v2)); return Integer.valueOf(value1).compareTo(value2); } }); } })); } return copies; } /** * Compute simple feedback arc set by performing |n| DFS traversals (each starting * with a different vertex) on the tangle, taking non-forward edges as feedback. * The minimum weight feedback arc set among those |n| results is returned. * @param tangle strongly connected component * @param weights edge weights * @return feedback arc set */ @Override protected <V,E> Digraph<V,E> lfas(Digraph<V,E> tangle, EdgeWeights<? super V> weights) { /* * store best results */ int minWeight = Integer.MAX_VALUE; int minSize = Integer.MAX_VALUE; List<V> minFinished = null; /* * threshold on max. number of iterations (avoid running forever) */ int maxIterationsLeft = Math.max(1, 1000000 / (tangle.getVertexCount() + tangle.getEdgeCount())); /* * perform DFS for each node, keep best result */ List<Digraph<V,E>> copies = copies(tangle, Math.min(10, tangle.getVertexCount())); List<V> finished = new ArrayList<V>(tangle.getVertexCount()); Set<V> discovered = new HashSet<V>(tangle.getVertexCount()); for (V start : tangle.vertices()) { for (Digraph<V, E> copy : copies) { finished.clear(); discovered.clear(); Digraphs.dfs(copy, start, discovered, finished); assert finished.size() == tangle.getVertexCount(); int weight = 0; int size = 0; discovered.clear(); for (V source : finished) { discovered.add(source); for (V target : tangle.targets(source)) { if (!discovered.contains(target)) { // feedback edge weight += weights.get(source, target); size++; } } if (weight > minWeight) { break; } } if (weight < minWeight || weight == minWeight && size < minSize) { minFinished = new ArrayList<V>(finished); minWeight = weight; minSize = size; } } if (--maxIterationsLeft == 0) { break; } } /* * create feedback graph */ Digraph<V, E> feedback = MapDigraph.<V, E>getDefaultDigraphFactory().create(); discovered.clear(); for (V source : minFinished) { discovered.add(source); for (V target : tangle.targets(source)) { if (!discovered.contains(target)) { // feedback edge feedback.put(source, target, tangle.get(source, target)); } } } return feedback; } }