Consider {<1,2>, <1,3>, <1,7>, <0,4>} as the set of tuples of a relation R. Now consider that R is represented (via its membership function) by a BDD. That is, The BDD representing R depends on variables {x1,x2, y1, y2, y3} where {x1, x2} are used to represent the first element of every tuple and {y1, y2, y3} are used to represent the second element.
Now, consider the problem of finding the set of tuples that have unique values in its first element. For the relation above that set would be {<0,4>}. All the other elements are discarded as they are more than one value having 1 in the first component.
As a second example consider the relation with set of tuples {<1,2>, <1,3>, <1,7>, <2,3>, <2,5>, <0,4>}. In such a case the expected result is still {<0,4>} as 2 appears more than once as first element.
The problem can be also seen as abstracting away the variables {y1,y2,y3} such that only unique values for {x1,x2} remain. With this result, the expected relation can be reconstructed by computing the conjunction of the resulting BDD with the input one.
In summary, the the question is: which are the BDD operations that have to be performed on The representation of R to obtain the BDD with only the unique tuples.
Notice that this is a genralization of this question
EDIT 1: The following code reflects the implementation I have so far. However, I am wondering if it is possible to get a more efficient version. For simplicity I intentionally omit the handling of the computed table (crucial to get better time complexity). Additionally, I use &, | and ! to denote the conjunction, disjunction and complement operations on BDDs.
BDD uniqueAbstract(BDD f, BDD cube) {
if ((f.IsZero() || f.IsOne()) && !cube.IsOne())
return zero();
BDD T = high(f);
BDD E = low(f);
if(level(f) == level(c)) { // current var is abstracted
BDD uniqueThen = uniqueAbstract(T, high(c));
BDD existElse = existAbstract(E, high(c));
BDD existThen = existAbstract(T, high(c));
BDD uniqueElse = uniqueAbstract(E, high(c));
return (uniqueThen & !existElse) | (uniqueElse & !existThen)
} else {
BDD uniqueThen = uniqueAbstract(T,c);
BDD uniqueElse = uniqueAbstract(E,c);
return ite(top(f), uniqueThen, uniqueElse);
}
}
EDIT2: After trying three different implementations there are still some performance issues. Let me describe the three of them.
- A C implementation of my approach, let me call it the reference implementation4.
- The implementation proposed by user meolic in the accepted answer3.
- A hybrid approach between the two and available2.
The goal of this update is to analyze a bit the results from using the three approaches. As time measures seem misleading at this time to judge them, I decided to evaluate the implementations on a different set of measures.
- Recursive calls
- Cache hits
- Abstract simple. Number of times the function call was solved without requiring existential abstraction.
- Abstract complex: Number of times the function call was solved requiring existential abstraction.
- Exist abstract: Number of calls to the existential abstraction.
The results for implementation 1: (21123 ms): Unique abstraction statistics: Recursive calls: 1728549.000000 Cache hits: 638745.000000 Non abstract: 67207.000000 Abstract simple: 0.000000 Abstract complex: 0.000000 Exist abstract: 1593430.000000
Results for implementation 2: (run time: 54727 ms) Unique abstraction statistics: Recursive calls: 191585.000000 Cache hits: 26494.000000 Abstract simple: 59788.000000 Abstract complex: 12011.000000 Exist abstract: 24022.000000
Results for implementation 3: (run time: 20215 ms) Unique abstraction statistics: Recursive calls: 268044.000000 Cache hits: 30668.000000 Abstract simple: 78115.000000 Abstract complex: 46473.000000 Exist abstract: 92946.000000
EDIT 3: The following results were obtained after implementing every logical operation in terms of ITE5.
uniqueAbstractRecRef (21831 ms) Unique abstraction statistics: Total calls: 1723239 Optimized calls: 0 Total exist abstract calls: 30955618 Unique abstract calls to exist abstract: 2385915 Total ite calls: 3574555 Out of the total time, uniqueAbstractRecRef takes 4001 ms (12.4%)
uniqueAbstractSERec (56761 ms) Unique abstraction statistics: Total calls: 193627 Optimized calls: 60632 Total exist abstract calls: 16475806 Unique abstract calls to exist abstract: 24304 Total ite calls: 1271844 Out of the total time, uniqueAbstractSERec takes 33918 ms (51.5%)
uniqueAbstractRec (20587 ms) Unique abstraction statistics: Total calls: 270205 Optimized calls: 78486 Total exist abstract calls: 13186348 Unique abstract calls to exist abstract: 93060 Total ite calls: 1256872 Out of the total time, uniqueAbstractRec takes 3354 ms (10.6%)