Question

I need an algorithm for recognizing an interval graph and generate its intervals. After some research I found the Algorithm developed by Wen-Lian Hsu.

(http://www.iis.sinica.edu.tw/IASL/webpdf/paper-1992-A_New_Test_for_Interval_Graphs.pdf).

It seems to be an algorithm, which solves my problem. But, I am not a computer scientist so I am having problems understanding the algorithm.

Could anybody explain this algorithm to a novice, plain and simple?

Was it helpful?

Solution

Having worked through some examples I think I know what is going on, though I still do not follow algorithm 4. My algorithm for determining if graphs are interval graphs is below followed by some Javascript code to implement the algorithm. Putting it in code allowed me to check whether it worked or not. The code can be found at this JSFiddle. I have tested the code with these three graphs. (1 and 2 are interval graphs 3 is not)

enter image description here

As I have made the algorithm from my interpretation of the paper given in my earlier answer I can give no guarantees that it is fully correct but it seems to work.

The algorithm will be worked through with graph 1

When x and y are vertices of the graph then x and y are neighbours when x and y are joined by an edge of the graph.

Algorithm for Interval Graphs.

Stage 1 create a Lexicographically Ordered list, L, of vertices from the graph.

Form an arbitrarily ordered list U of vertices of the graph, called a CLASS.

Form US, an ordered list of one element the class U.

While US is not empty

Take the 1st vertex, v,  from the 1st class in US and put it at the front of L.

Set T to be an empty class

For each vertex in each class in US 

If it is a neighbour of v remove it from its class and push to back of T

If T is not empty put T at front of US.

Remove any empty classes from US

L is now a Lexicographically Ordered list of vertices from the graph

For graph 1

U= (3,6,1,4,5,8,2,7)

US=( (3,6,1,4,5,8,2,7))

v=3 US=( (3,6,1,4,5,8,2,7)) L=(3)

neighbours of 3 to front

US=( (6,8),(1,4,5,2,7))

v=6 US=((8)(1,4,5,2,7)) L=(6,3)

neighbours of 6 to front

US=((8,1,7),(4,5,2))

v=8 US=((1,7)(4,5,2)) L=(8,6,3)

neighbours of 8 to front

US=((7,4,5,2)(1))

v=7 US=((4,5,2)(1)) L=(7,8,6,3)

neighbours of 7 to front

US=( (4,5)(2)(1))

v=4 US=((5)(2)(1)) L=(4,7,8,6,3)

neighbours of 4 to front

US=((5)(2)(1))

v=5 US=((2)(1)) L=(5,4,8,6,3)

neighbours of 5 to front – no neighbours so no change

US=((2)(1))

v=2 US=((1)) L=(2,5,4,8,6,3)

neighbours of 2 to front – no neighbours so no change

US=((1))

v=1 US=() L=(1,2,5,4,8,6,3)

L finished

Stage 2 – First test for an Interval Graph

When x is a vertex of the graph and L a lexicographical Ordered list of the graph

Set RN(x) to be the neighbours of x which come after x in L placed in the same order that they occur in L

Set parent(x) to be the first vertex in RN(x)

Set RNnoP(x) to be the vertices of in RN(x) with parent(x) removed, ie RN(x)/parent(x)

If the graph is an interval graph then for all vertices x if RNnoP(x) has any vertices in it then they will all appear in RN(parent(x)), ie RNnoP(x) is a subset of RN(parent(x)

If any x fails this test then the graph cannot be an interval graph.

Below shows the results for the example graph

x    RN(x)    Parent(x)    RN(x)/parent(x)    RN(parent(x))    Pass

1   | 6    |     6         |     -    |            3-      |   T

2   | 8    |     8         |     -    |            6-      |   T

5   | 7,8  |     7         |     8    |            8-      |   T

4   | 7,8  |     7         |     8    |            8-      |   T

7   |8     |     8         |     -    |            6-      |   T

8   | 6,3  |     6         |     3    |            3-      |   T

6   | 3    |     3         |     -    |            --      |   T

3   |  -   |     -         |     -    |            --      |   T

Stage 3 - for graphs that pass stage 2 form cliques for each vertex of the graph and create a set of maximal cliques

A CLIQUE is a set of vertices from the graph such that for any pair of different vertices in the set x,y then x and y are neighbours.

Set C(x) to be the set containing the vertices in RN(x) together with x, ie RN(x) Union {x}

Now it is necessary to form a set of maximal cliques. A clique is MAXIMAL if the addition on any other vertex into the clique stops it being a clique.

Using the parent relationships found in stage 2 form a tree

Using post-order traverse the tree and form a set, CS, of vertices where for each x in CS C(x) is a maximal clique using the following process on all vertices except for the root. 

If C(parent(x)) is a subset of C(x) 

if parent(x)  in CS remove it

put x at in CS 

Below is a tree for the example graph showing x and C[x] at each node

enter image description here

x    RN(x)    Parent(x)    C(x)    C(parent(x))

1    |6    |  6          |  1,6      |   3,6

2    Z8    |  8          |  2,8      |   3,6,8

5    |7,8  |  7          |  5,7,8    |   7,8

4    |7,8  |  7          |  4,7,8    |   7,8

7    |8    |  8          |  7,8      |   3,6,8

8    |6,3  |  6          |  3,6,8    |   3,6

6    |3    |  3          |  3,6      |   3

3    | -   |  -          |  3        |   -

The process on above tree

x=3 is root

x=6 C(6) = {3,6} CS=(6) C(3) is a subset of C(6) but 3 not in OC

x=1 C(1)={1,6} CS=(1,6) C(6) not a subset of C(1) put in 6

x=8 C(8)={3,6,8) CS=(1,8) C(6) is a subset of C(8) remove 6 put in 8

x=7 C(7)={7,8} ) CS=(1,8,7) C(8) not a subset of C(7) put in 7

x=4 C(4)={4,7,8} CS=(1,8,4) C(7) is a subset of C(4) remove 7 put in 4

x=5 C(5)={5,7,8} CS=(1,8,4,5) C(7) is a subset of C(5) but no 7, put in 5

x=2 C(2)={2,8} CS={1,8,4,5,2} C(8) is not a subset of C(2) put in 2

NOTE in the code I used a Children’s relationship to traverse the tree as a way of excluding the root.

Stage 4 Attempt to order the maximal cliques so that they are consecutive.

Cliques are in CONSECUTIVE order if for any vertex x, if x is in cliques n and n+m, m>0, the x is in cliques n+1, n+2, ……n+ m-1

The following algorithm will put the maximal cliques in consecutive order if it is possible to do so

Set NC to be the ordered list, or class, of maximal cliques from CS, such that if x is in CS(x) then C(x) is in NC

Set P to be the ordered list containing NC, P=(NC)

Set OC=() and empty ordered list

While P is not empty

    Take the last clique, LST, from the last class of P and put at front of OC

    For each clique Q in the last class of P partition into two classes 

        OUT if Q and LST have no vertices in common (intersection empty)

IN if Q and LST have vertices in common (non empty intersection)

Replace the last class of P with the classes OUT IN if both non empty

Replace the last class of P with the class OUT if OUT non empty and IN empty

Replace the last class of P with the classes IN if IN non empty and OUT  empty

Leave P if both empty

For the example graph P=(({3,6,8},{4,7,8},{1,6},{5,7,8},{2,8})) (I have mixed up the order to show how the process works)

P=(({3,6,8},{4,7,8},{1,6},{5,7,8},{2,8})) OC=()

P=(({3,6,8},{4,7,8},{1,6},{5,7,8})) OC=({2,8})

OUT=({1,6}) IN=({3,6,8},{4,7,8},{5,7,8})

P=(({1,6}),({3,6,8},{4,7,8},{5,7,8})) OC=({2,8})

P=(({1,6}),({3,6,8},{4,7,8})) OC=({5,7,8},{2,8})

OUT=() IN({3,6,8},{4,7,8})

P=(({1,6}),({3,6,8},{4,7,8})) OC=({5,7,8},{2,8})

P=(({1,6}),({3,6,8})) OC=({4,7,8},{5,7,8},{2,8})

OUT=() IN({3,6,8})

P=(({1,6}),({3,6,8})) OC=({4,7,8},{5,7,8},{2,8})

P=(({1,6})) OC=({3,6,8},{4,7,8},{5,7,8},{2,8})

OUT=() IN=({1,6})

P=(({1,6})) OC=({3,6,8},{4,7,8},{5,7,8},{2,8})

P=(()) OC=({1,6},{3,6,8},{4,7,8},{5,7,8},{2,8})

P=()

NOTE in the code I have left NC = cliques as a list of vertices and used clique(label) for C(x)

Stage 5 check if OC is consecutively ordered

For each v in CS (as in stage 3)

    For each vertex, x in C(v) the clique associated with v

    If x is only in adjacent cliques in OC then
        Interval graph
    else
          Not an interval graph

Stage 6 if past consecutive test draw intervals

Where n is the number of vertices determine n columns numbered 1 to n with gaps between them and equal width

For each v in CS with v first appearing in clique i and lastly in clique j 1=i<=j=n draw the interval from the start of column i to the end of column j

CODE IN JAVASCRIPT

Styles for Output of Intervals

.interval {
    border-bottom: 1px solid black;
    position: absolute
}

.label {
    position:absolute;
}

Code

//Array methods added to carry out interval graph check

if(!Array.indexOf){  //Needed for earlier versions of IE;
    Array.prototype.indexOf = function(obj){
        for(var i=0; i<this.length; i++){
            if(this[i]===obj){
                return i;
            }
        }
        return -1;
    }
}

Array.prototype.subsetOf = function(set){ //returns true if Array is a subset of set
    if(this.length==0) {return true;}  //empty set is subset of all sets
    var subset=true;
    for(var i=0; i<this.length; i++){
           subset = subset && (set.indexOf(this[i])>-1); //element of Array not in set forces subset to be false
       }
       return subset;
}

Array.prototype.intersection = function(set){ //returns the intersection of Array and set
    if(this.length==0) {return [];}  //empty set intersect any set is empty set
    var inter=[];
    for(var i=0; i<this.length; i++){
           if(set.indexOf(this[i])>-1) {//element of Array and set
    inter.push(this[i]);
           } 
    }
    return inter;
}

Array.prototype.union = function(set){ //returns the union of Array and set
    var union=[];
    for(var i=0; i<this.length; i++){
        union.push(this[i]);
    }
    for(var i=0; i<set.length; i++) {
           if(union.indexOf(set[i])==-1) {//element not yet in union
              union.push(set[i]);
           } 
    }
    return union;
}

//A set is an array with no repeating elements


function vertex(label,neighbours) {
    this.label=label;
    this.neighbours=neighbours;
}

//Using the following format for each vertex on the graph [vertex lable, [neighbour lables] ] set up the model of the graph
//order of vertices does not matter

//graph One - an interval graph
graph=[
    [3,[6,8]],
    [6,[1,3,7,8]],
    [1,[6]],
    [4,[7,8]],
    [5,[7,8]],
    [8,[2,3,4,5,6,7]],
    [2,[8]],
    [7,[4,5,8]]
];

//graph Two - an interval graph
/*graph=[
    ['A',['B','C','D']],
    ['B',['A','C']],
    ['C',['A','B','D','E','F']],
    ['D',['A','C','E','F']],
    ['E',['C','D']],
    ['F',['D','G']],
    ['G',['F']]
];


//graph Three - not an interval graph
graph=[
    ['W',['Y','Z']],
    ['X',['Z']],
    ['Y',['W']],
    ['Z',['W']]
];

*/
/*Create a new vertex object U[i] where U is an unordered array.
 *Unordered as at this point the numbering of vertices does not matter.
 *Referencing by name rather than an array index is easier to follow
 */

var U=[];

for(var i=0; i<graph.length; i++) {
    U[i]=new vertex(graph[i][0],graph[i][4]);
}

var US=[U]; 
/*US is an array containing the single array U
 * during Lexicographical ordering US will contain other arrays
 */ 

//********************Lexicographical Ordering Start*************************

var L=[]; //L with contain the vertices in Lexicographical order.

while (US.length>0) {    
    F=US[0]; //First array in US    
    vertex=F.shift(); //In Javascript shift removes first element of an array    
    if(F.length==0) { //F is empty
        US.shift();
    }
    L.unshift(vertex); //In Javascript unshift adds to front of array    
    var T=new Array(); //new array to add to front of US
    tus=[]; //tempory stack for US sets    
    while(US.length>0) { //for remaining vertices in the arrays in US check if neighbours of vertex        
        set=US.shift(); //first set of US
        ts=[]; //tempory stack for set elements        
        while(set.length>0){
           v=set.shift();  //v is one of the remaining vertices //
           lbl=v.label;    
           if (vertex.neighbours.indexOf(lbl) != -1) { //is v a neighbour of vertex
               T.push(v);      //push v to T  
           }
           else {
              ts.unshift(v);  //not a neighbour store for return
           }
        }
        while(ts.length>0) {    //restore list of v not moved to set           
           set.push(ts.pop());
        }
        if(set.length>0) {//if set not empty store for restoration
              tus.unshift(set);
        }
    }
    if(T.length>0) {   //if T not empty
        US.push(T); //put T as first set of US
    }
    while(tus.length>0) {
        US.push(tus.pop()); // restore non empty sets
    }
}


//************************End of Lexicographical Ordering*************************

//----------------------Chordality Check and Clique Generation Start----------------------------------------
RN={}; //RN as an object so that an associative array can be used if labels are letters
Parent={};  //Parent as an object so that an associative array can be used if labels are letters
RNnoP={}; //RN with parent removed as an object so that an associative array can be used if labels are letters
Children={}; //Used in clique generation NOTE this is a deviation from given alogorithm 4 which I do not follow, again object for associative array
for(var i=0; i<L.length;i++) {
    Children[L[i].label]=[];
}
var chordal=true; 
for(var i=0; i<L.length-1; i++) {
    vertex=L[i];
    RN[vertex.label]=[];
    RNnoP[vertex.label]=[];
    for(j=i+1;j<L.length; j++) {
        v=L[j];
        lbl=v.label;
        if(vertex.neighbours.indexOf(lbl) != -1) {
            RN[vertex.label].push(lbl); //store vertex labels in order they are processed
        }
    }
    Parent[vertex.label]=RN[vertex.label][0]; //Parent is front vertex of RN
    Children[RN[vertex.label][0]].push(vertex.label);//used for Clique generation my method
    for(k=1;k<RN[vertex.label].length;k++) {
        RNnoP[vertex.label][k-1]=RN[vertex.label][k];
    } 
}
//**************  chordality check ************
for(i=0; i<L.length-1; i++) {
    vertex=L[i];
    var x=vertex.label;
    parentx=Parent[x];
    for(j=0;j<RNnoP[x].length;j++) {
        chordal = chordal && (RNnoP[x].subsetOf(RN[parentx]));
    }
}

if(!chordal) {
    alert('Not an Interval Graph');
}
else {
    //Construct maximal clique list from tree formed by parent and child relationships determined above NOTE not algorithm 4    
    var root = Children[L[L.length-1].label]; //last vertex in L which has no parent
    RN[L[L.length-1].label]=[]; //no vertices to right of last vertex
    var clique={}; //clique for each vertex label -- object so associative array using lables
    var cliques=[]; //stores maximal cliques from subtree of vertices processed
    clique[L[L.length-1].label]=[L[L.length-1].label]; //clique for root contains last vertex label
    generateCliques(root);  //cliques becomes a list of labels of vertices with maximal cliques
    var pivots=[];
    for(i=0;i<cliques.length;i++) {    
        pivots=pivots.union(clique[cliques[i]]);    
    }
    /*attempt to place each clique in cliques in consecutive order 
     * ie all cliques containing a given label are all next to each other 
     * if at end of process the cliques are in consecutive order then have an interval graph otherwise not an interval graph
    */

    var orderedCliques=[]; //will contain maximal cliques in consecutive order if possible
    var partitions=[cliques]; //holds partitions of cliques during process
    while(partitions.length>0) { 
        inPartition=new Array(); //partition of elements containing pivot
        outPartition=new Array(); //partition of elements not containing pivot
        lastPartition=partitions.pop(); //last partition of cliques        
        orderedCliques.unshift(lastPartition.shift());//first label in partition moved to front of orderedCliques    
        pivotClique=clique[orderedCliques[0]]; //which points to pivot clique    
        for(var i=0; i<lastPartition.length; i++) {         
            if(pivotClique.intersection(clique[lastPartition[i]]).length>0){   //non empty intersection
               inPartition=inPartition.union([lastPartition[i]]);
            }
            else {
               outPartition=outPartition.union([lastPartition[i]]);
            }        
        }
        if(outPartition.length>0) {
            partitions.push(outPartition);
        }
        if(inPartition.length>0) {    
             partitions.push(inPartition);
        }
    }

    //----------------------End of Chordality Check and Clique Generation----------------------------------------
    var start={};  //start is an associative array;
    var end={};    //end is an associative array;
    if (consecutive()){
        //draw intervals......................
        var across=20;
        var down=20;
        var colwidth=20;
        var gap=30;
        var coldepth=30;
        var height=20;
        for(v=0;v<pivots.length;v++) {
           var vertex=pivots[v];
           var line=document.createElement('div');
           line.style.top=(down+(coldepth+height)*v)+'px';        
           line.style.height=(coldepth+height)+'px';
           line.style.left=(across+start[vertex]*(colwidth+gap))+'px';
           line.style.width=((end[vertex]-start[vertex])*gap+(1+end[vertex]-start[vertex])*colwidth)+'px';
           line.className='interval';
           document.body.appendChild(line);
           var label=document.createElement('div');
           label.style.left=line.style.left;
           label.style.top=(parseInt(line.style.top)+28)+'px';
           label.style.height='17px';
           label.style.width='30px';
           label.innerHTML=vertex;
           label.className='label';
           document.body.appendChild(label);
        }
    }
    else {
        alert('Not an Interval Graph')
    };  
}

function generateCliques(node) {    
    for(var i=0; i<node.length;i++) {
        lbl=node[i];
        clique[lbl]=[];
        for(j=0;j<RN[lbl].length;j++) {
            clique[lbl][j]=RN[lbl][j]; //each element of RN[x] becomes an element of clique[x]
        }
        clique[lbl].push(lbl); //RN(x) U {x} is a clique of subgraph processed so far and now   clique[x] = RN[x] as sets
        var parentx=Parent[lbl];
        if(clique[parentx].subsetOf(clique[lbl])) {
            var indx=cliques.indexOf(parentx);
            if(indx>-1) { //if parent of lbl is in cliques list remove it
                cliques.splice(indx,1);
            }
        }
        cliques.push(lbl); //add lbl to cliques list
        if(Children[lbl].length>0) {  //lbl is not a leaf
             generateCliques(Children[lbl]);
        }
    }
}

function consecutive() {
    var p;
    for(v=0;v<pivots.length;v++) {
        var vertex=pivots[v];
        p=0;
        for(cl=0;cl<orderedCliques.length;cl++) {
            if(clique[orderedCliques[cl]].indexOf(vertex)>-1) { //is vertex in maximal clique
                if(p==0){
                   p=cl+1;
                   start[vertex]=p;
                   if(p==orderedCliques.length) {
                      end[vertex]=p;
                   }
            }
            else {
                   p+=1;        
                   if(p==orderedCliques.length) {
                       end[vertex]=p;
                   }
                   if(p!=cl+1) {
                       return false;
                   }
               }
           }
           else {
              if(!end[vertex] && p>0) {
                   end[vertex]=cl;
              }
           }
        }
    }
    return true;
}

OTHER TIPS

This is not the full answer you are looking for but I hope it goes some way to helping.

Wikipedia led me to the *Interval Graph * page, on to the Lexicographic breadth-first search page on which I found the reference to the paper Habib, Michel; McConnell, Ross; Paul, Christophe; Viennot, Laurent (2000), "Lex-BFS and partition refinement, with applications to transitive orientation, interval graph recognition and consecutive ones testing",

Now this paper does give actual algorithms for determining if a graph is an integral graph using algorithms 2,3,4 and 9. Algorithms 2 and 3 can be found in alternative forms on the LBS page above and can be worked through. However so far, over the last couple of days, algorithm 4 has defeated me. Even working through the example graph they give does not produce the results they state.

So three possibilities.

  1. I am not clever enough to understand it;
  2. The algorithm is not sufficiently detailed;
  3. There are mistakes in the algorithm.

Working on it being 2 or 3 that is true I will continue working on it on and off to see if I can crack it. Then there is algorithm 9 to tackle.

Perhaps the above pages and paper will give you enough insight into solving your problem. If I find a full answer I will post it. Good Luck.

For those who suffer from this paper like I do, I confirm that the algorithm 4 in the mentioned reference paper is weird/broken. Instead, I have found the second paper from the same authors about the same topics. You can check both papers here: http://citeseer.uark.edu:8080/citeseerx/showciting;jsessionid=B9CECB9E4B9DA156C687A414FA8743BF?cid=1681311

The second one appears to be written after a month and seems to be corrected by authors. I hope this may help someone now or later. In case the mentioned link will be unavailable ever, here are the 2 headings of the papers to search for:

  1. Lex-BFS and Partition Refinement, with Applications to Transitive Orientation, Interval Graph Recognition and Consecutive Ones Testing.
  2. Lex-BFS, a Partition Refining Technique. Application to Transitive Orientation, Interval Graph Recognition and Consecutive 1's Testing.

I have implemented the algorithms described in the second paper but it appears to have some bugs in the algorithm. I have met one of the authors (prof. Michel Habib) regarding to this, which required some more deep analysis. My implementation can be found here: https://github.com/Hack06/LexBFS

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top