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

org.chocosolver.graphsolver.util.LCAGraphManager Maven / Gradle / Ivy

There is a newer version: 4.2.2
Show newest version
/**
 * Copyright (c) 2015, Ecole des Mines de Nantes
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by the .
 * 4. Neither the name of the  nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY  ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL  BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.chocosolver.graphsolver.util;

import org.chocosolver.util.objects.graphs.DirectedGraph;
import org.chocosolver.util.objects.setDataStructures.ISet;

import java.util.Iterator;

/**
 * Class enabling to compute LCA queries in constant time over the DFS tree of a given graph
 * use a O(n+m) time preprocessing
 *
 * @author Jean-Guillaume Fages
 */
public class LCAGraphManager {

	//***********************************************************************************
	// VARIABLES
	//***********************************************************************************

	//
	private int root;
	private DirectedGraph graph;
	private int nbNodes, nbActives;
	//
	private int[] father;
	private int[] nodeOfDfsNumber;
	private int[] dfsNumberOfNode;
	//
	private int[] I, L, h, A, htmp;
	private ISet[] successors;
	private Iterator[] iterator;

	//***********************************************************************************
	// CONSTRUCTORS
	//***********************************************************************************

	public LCAGraphManager(int nb) {
		nbNodes = nb;
		successors = new ISet[nbNodes];
		father = new int[nbNodes];
		nodeOfDfsNumber = new int[nbNodes];
		dfsNumberOfNode = new int[nbNodes];
		htmp = new int[nbNodes];
		A = new int[nbNodes];
		I = new int[nbNodes];
		L = new int[nbNodes];
		h = new int[nbNodes];
		iterator = new Iterator[nbNodes];
	}

	public void preprocess(int r, DirectedGraph g) {
		root = r;
		graph = g;
		initParams();
		proceedFirstDFS();
		performLCAPreprocessing();
	}

	//***********************************************************************************
	// QUERIES
	//***********************************************************************************

	public int getLCA(int x, int y) {
		return nodeOfDfsNumber[getDFS_LCA(dfsNumberOfNode[x], dfsNumberOfNode[y])];
	}

	//***********************************************************************************
	// INITIALIZATION
	//***********************************************************************************

	private void initParams() {
		nbActives = graph.getNodes().size();
		for (int i = 0; i < nbNodes; i++) {
			successors[i] = graph.getSuccOf(i);
			dfsNumberOfNode[i] = -1;
			father[i] = -1;
			A[i] = -1;
		}
	}

	/**
	 * perform a dfs in graph to label nodes
	 */
	private void proceedFirstDFS() {
		for(int i=0;iI(v) pointe sur le dernier element, L(I(v)) vers le premier)
	 * o	The head of a run is identified when computing I values. v is identified as the head of its run if the I value of v's parent is not I(v).
	 * o	After this step, the head of a run containing an arbitrary node v can be located in constant time. First compute I(v) then look up the value L(I(v)).
	 * 3.	Given a complete binary tree with node-depth ceiling(log n)-1, map each node v in the general tree to I(v) in the binary tree (Fig 4).
	 * 4.	For each node v in the general tree create on O(log n) bit number A v. Bit A v(i) is set to 1 if and only if node v has an ancestor
	 * in the general tree that maps to height i in the binary tree. i.e. iff v has an ancestor u such that h(I(u)) = i.
	 */
	private void performLCAPreprocessing() {
		// step 1 : DFS already done
		// step 2
		ISet nei;
		for (int i = nbActives - 1; i >= 0; i--) {
			h[i] = BitOperations.getFirstExp(i + 1);
			if (h[i] == -1) {
				throw new UnsupportedOperationException();
			}
			htmp[i] = h[i];
			I[i] = i;
			L[i] = i;
			int sucInRun = -1;
			nei = graph.getSuccOf(nodeOfDfsNumber[i]);
			int s;
			for (int k : nei) {
				s = dfsNumberOfNode[k];
				if (i != s && father[s] == i && htmp[s] > htmp[i]) {
					htmp[i] = htmp[s];
					sucInRun = s;
				}
			}
			if (sucInRun != -1) {
				I[i] = I[sucInRun];
				L[I[i]] = i;
			}
		}
		// step 3 : mapping to a binary tree : the binary tree is virtual here so there is nothing to do
		//step 4
		// A[0] = I[0];
		int exp = BitOperations.getFirstExp(I[0] + 1);
		if (exp == -1) {
			throw new UnsupportedOperationException();
		}
		A[0] = BitOperations.pow(2, exp);
		for (int i = 0; i < nbActives; i++) {
			A[i] = A[father[i]];
			if (I[i] != I[father[i]]) {
				exp = BitOperations.getFirstExp(I[i] + 1);
				if (exp == -1) {
					throw new UnsupportedOperationException();
				}
				A[i] += BitOperations.pow(2, exp);
			}
		}
	}

	/**
	 * Get the lowest common ancestor of two nodes in O(1) time
	 * Query by Chris Lewis
	 * 1.	Find the lowest common ancestor b in the binary tree of nodes I(x) and I(y).
	 * 2.	Find the smallest position j ≥ h(b) such that both numbers A x and A y have 1-bits in position j.
	 * This gives j = h(I(z)).
	 * 3.	Find node x', the closest node to x on the same run as z:
	 * a.	Find the position l of the right-most 1 bit in A x
	 * b.	If l = j, then set x' = x {x and z are on the same run in the general graph} and go to step 4.
	 * c.	Find the position k of the left-most 1-bit in A x that is to the right of position j.
	 * Form the number consisting of the bits of I(x) to the left of the position k,
	 * followed by a 1-bit in position k, followed by all zeros. {That number will be I(w)}
	 * Look up node L(I(w)), which must be node w. Set node x' to be the parent of node w in the general tree.
	 * 4.	Find node y', the closest node to y on the same run as z using the approach described in step 3.
	 * 5.	If x' < y' then set z to x' else set z to y'
	 *
	 * @param x node dfs number
	 * @param y node dfs number
	 * @return the dfs number of the lowest common ancestor of a and b in the dfs tree
	 */
	private int getDFS_LCA(int x, int y) {
		// trivial cases
		if (x == y || x == father[y]) {
			return x;
		}
		if (y == father[x]) {
			return y;
		}
		// step 1
		int b = BitOperations.binaryLCA(I[x] + 1, I[y] + 1);
		// step 2
		int hb = BitOperations.getFirstExp(b);
		if (hb == -1) {
			throw new UnsupportedOperationException();
		}
		int j = BitOperations.getFirstExpInBothXYfromI(A[x], A[y], hb);
		if (j == -1) {
			throw new UnsupportedOperationException();
		}
		// step 3 & 4
		int xPrim = closestFrom(x, j);
		int yPrim = closestFrom(y, j);
		// step 5
		if (xPrim < yPrim) {
			return xPrim;
		}
		return yPrim;
	}

	private int closestFrom(int x, int j) {
		// 3.a
		int l = BitOperations.getFirstExp(A[x]);
		if (l == -1) {
			throw new UnsupportedOperationException();
		}
		if (l == j) {    // 3.b
			return x;
		} else {        // 3.c
			int k = BitOperations.getMaxExpBefore(A[x], j);
			int IW = I[x] + 1;
			if (k != -1) { // there is at least one 1-bit at the right of j
				IW = BitOperations.replaceBy1and0sFrom(IW, k);
			} else {
				throw new UnsupportedOperationException();
			}
			IW--;
			return father[L[IW]];
		}
	}

	//***********************************************************************************
	// ACCESSORS
	//***********************************************************************************

	/**
	 * Get the parent of x in the dfs tree
	 *
	 * @param x the focused node
	 * @return the parent of x in the dfs tree
	 */
	public int getParentOf(int x) {
		return nodeOfDfsNumber[father[dfsNumberOfNode[x]]];
	}

	/**
	 * @return an array containing the nodes sorted according to the dfs order
	 */
	public int[] getNodeOfDfsNumber() {
		return nodeOfDfsNumber;
	}

	/**
	 * @return an array containing the dfs numbers of nodes
	 */
	public int[] getDfsNumberOfNode() {
		return dfsNumberOfNode;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy