
org.logicng.knowledgecompilation.bdds.jbuddy.BDDReordering Maven / Gradle / Ivy
///////////////////////////////////////////////////////////////////////////
// __ _ _ ________ //
// / / ____ ____ _(_)____/ | / / ____/ //
// / / / __ \/ __ `/ / ___/ |/ / / __ //
// / /___/ /_/ / /_/ / / /__/ /| / /_/ / //
// /_____/\____/\__, /_/\___/_/ |_/\____/ //
// /____/ //
// //
// The Next Generation Logic Library //
// //
///////////////////////////////////////////////////////////////////////////
// //
// Copyright 2015-20xx Christoph Zengler //
// //
// 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 org.logicng.knowledgecompilation.bdds.jbuddy;
import static org.logicng.knowledgecompilation.bdds.jbuddy.BDDKernel.MARKHIDE;
import static org.logicng.knowledgecompilation.bdds.jbuddy.BDDTree.addRange;
import org.logicng.util.Pair;
import java.util.Arrays;
import java.util.Random;
/**
* This class encapsulates the reordering mechanism on BDDs.
*
* The class is initialized with the BDD kernel on which it performs all operations.
* This means that all reordering operations initiated on this object will affect
* all BDDs created by its kernel.
*
* There are three types operations which can be performed here:
*
* - Swapping two variables in the kernel can be performed via {@link #swapVariables}
* - Reordering all variables can be performed via {@link #reorder}
* - Reordering during construction of the BDD can be configured via {@link #setReorderDuringConstruction}
*
* The last two operations only have an effect, if variable blocks were added. {@link #addVariableBlock(int, int, boolean) The docuentation}
* gives more information on variable blocks.
* To make all variables freely movable, {@link #addVariableBlockAll()} can be used.
* @version 2.0.0
* @since 2.0.0
*/
public class BDDReordering {
protected final BDDKernel k;
/* Current auto reord. method and number of automatic reorderings left */
protected BDDReorderingMethod reorderMethod;
protected int bddreorderTimes;
/* Flag for disabling reordering temporarily */
protected boolean reorderDisabled;
/* Store for the variable relationships */
protected BDDTree varTree;
protected int blockId;
/* Store for the ref.cou. of the external roots */
protected int[] extRoots;
protected int extRootSize;
protected LevelData[] levels; /* Indexed by variable! */
/* Interaction matrix */
protected InteractionMatrix interactionMatrix;
/* Number of live nodes before and after a reordering session */
protected int usednumBefore;
protected int usednumAfter;
/* Flag telling us when a node table resize is done */
protected boolean resizedInMakenode;
protected int usedNodesNextReorder;
/**
* Creates a new reordering object for the given kernel.
* @param k the kernel
*/
public BDDReordering(final BDDKernel k) {
this.k = k;
init();
}
protected void init() {
this.reorderDisabled = false;
this.varTree = null;
clrVarBlocks();
setReorderDuringConstruction(BDDReorderingMethod.BDD_REORDER_NONE, 0);
this.usednumBefore = this.usednumAfter = 0;
this.blockId = 0;
}
/**
* Swaps the variables {@code v1} and {@code v2}. This affects all BDDs
* created by {@link #k the kernel}.
* @param v1 the first variable to swap
* @param v2 the second variable to swap
*/
public void swapVariables(int v1, int v2) {
int l1, l2;
/* Do not swap when variable-blocks are used */
if (this.varTree != null) {
throw new IllegalStateException("Swapping variables is not allowed with variable blocks");
}
/* Don't bother swapping x with x */
if (v1 == v2) {
return;
}
/* Make sure the variable exists */
if (v1 < 0 || v1 >= this.k.varnum) {
throw new IllegalArgumentException("Unknown variable number: " + v1);
}
if (v2 < 0 || v2 >= this.k.varnum) {
throw new IllegalArgumentException("Unknown variable number: " + v2);
}
l1 = this.k.var2level[v1];
l2 = this.k.var2level[v2];
/* Make sure v1 is before v2 */
if (l1 > l2) {
final int tmp = v1;
v1 = v2;
v2 = tmp;
l1 = this.k.var2level[v1];
l2 = this.k.var2level[v2];
}
reorderInit();
/* Move v1 to v2's position */
while (this.k.var2level[v1] < l2) {
reorderVardown(v1);
}
/* Move v2 to v1's position */
while (this.k.var2level[v2] > l1) {
reorderVarup(v2);
}
reorderDone();
}
/**
* Reorders the levels in the kernel using the given reordering method.
* Only blocks of variables will be reordered. See the documentation of
* {@link #addVariableBlock} to learn more about such variable blocks.
* Without the definition of any block, nothing will be reordered.
*
* If the reordering should be performed without any restrictions,
* {@link #addVariableBlockAll()} can be called before this method.
* @param method the method to be used for the reordering
*/
public void reorder(final BDDReorderingMethod method) {
final BDDTree top;
final BDDReorderingMethod savemethod = this.reorderMethod;
final int savetimes = this.bddreorderTimes;
this.reorderMethod = method;
this.bddreorderTimes = 1;
top = new BDDTree(-1);
if (reorderInit() < 0) {
return;
}
this.usednumBefore = this.k.nodesize - this.k.freenum;
top.setFirst(0);
top.setLast(this.k.varnum - 1);
top.setFixed(false);
top.setNext(null);
top.setNextlevel(this.varTree);
reorderBlock(top, method);
this.varTree = top.getNextlevel();
this.usednumAfter = this.k.nodesize - this.k.freenum;
reorderDone();
this.reorderMethod = savemethod;
this.bddreorderTimes = savetimes;
}
/**
* Activates or deactivates the automatic reordering during the construction of a BDD.
*
* Automatic reordering can be deactivated by passing {@link BDDReorderingMethod#BDD_REORDER_NONE}
* for the {@code method} parameter, otherwise the reordering is activated with the
* given method. The reordering is executed at most {@code num} times.
* @param method the method to be used for reordering
* @param num the maximum number of reorders to be performed
*/
public void setReorderDuringConstruction(final BDDReorderingMethod method, final int num) {
this.reorderMethod = method;
this.bddreorderTimes = num;
}
/**
* Adds a variable block starting at variable {@code first} and ending in variable
* {@code last} (both inclusive).
*
* Variable blocks are used in the {@link #reorder BDD reordering}
* or in the automatic reordering during the construction of the BDD (configured by
* {@link #setReorderDuringConstruction}). Variable blocks can be nested, i.e. one block can
* contain an arbitrary number of ("child") blocks. Furthermore, a variable block can also
* be a single variable.
*
* During reordering, the child blocks of a parent block can be reordered, but they are kept
* together. So no other block can be moved in between the child blocks. Furthermore,
* variables in a block which are not in a child block will be left untouched.
*
* Example: Lets assume we have a BDD with the variable ordering {@code v1, v2, v3, v4, v5, v6, v7}.
* We create the following blocks:
*
* - {@code A} reaching from {@code v1} to {@code v5}
* - {@code B} reaching from {@code v6} to {@code v7}
* - {@code A1} reaching from {@code v1} to {@code v2}
* - {@code A2} reaching from {@code v3} to {@code v3}
* - {@code A3} reaching from {@code v4} to {@code v5}
*
* This means that the variables of {@code A} and {@code B} can never be mixed up in the order.
* So during reordering the variables {@code v6} and {@code v7} can either be moved to the
* front (before {@code A}) or remain at their position.
* Furthermore, for example {@code v1} and {@code v2} will always stay together and neither
* {@code v3} nor any other variable can be moved in between them. On the other hand, the blocks
* {@code A1}, {@code A2}, and {@code A3} can be swapped arbitrarily.
*
* These are valid result of a reordering based on the above blocks:
*
* - {@code v3, v1, v2, v4, v5, v6, v7}
* - {@code v6, v7, v4, v5, v3, v1, v2}
* - {@code v6, v7, v1, v2, v3, v4, v5}
*
* These however would be illegal:
*
* - {@code v2, v1, v3, v4, v5, v6, v7} (variables in a block which are not in a child block will not be reordered)
* - {@code v1, v3, v2, v4, v5, v6, v7} (variables of different block will not be mixed up)
*
*
* If a block is fixed (the example above assumed always blocks which are not fixed), its
* immediate child blocks will remain in their order. E.g. if block {@code A} was fixed, the blocks
* {@code A1}, {@code A2}, and {@code A3} would not be allowed to be swapped.
* Let's assume block {@code A} to be fixed and that we have two other unfixed blocks:
*
* - {@code A11} reaching from {@code v1} to {@code v1}
* - {@code A12} reaching from {@code v2} to {@code v2}
*
* These are examples for legal reorderings:
*
* - {@code v2, v1, v3, v4, v5, v6, v7} (block {@code A} is fixed, but "grandchildren" are still allowed to be reordered
* - {@code v6, v7, v2, v1, v3, v4, v5}
*
* These are examples for illegal reorderings:
*
* - {@code v3, v2, v1, v4, v5, v6, v7} (block {@code A} is fixed, so it's child blocks must be be reordered
* - {@code v1, v2, v4, v5, v3, v6, v7}
*
*
* Each block (including all nested blocks) must be defined by a separate call to this method. The blocks
* may be added in an arbitrary order, so it is not required to add them top-down or bottom-up.
* However, the blocks must not intersect, except of one block containing the other. Furthermore,
* both the {@code first} and the {@code last} variable must be known by the kernel and the level {@code first}
* must be lower than the level of {@code last}.
* @param first the variable at which the block starts (inclusive)
* @param last the variable at which the block ends (inclusive)
* @param fixed whether the block should be fixed or not
*/
public void addVariableBlock(final int first, final int last, final boolean fixed) {
if (first < 0 || first >= this.k.varnum || last < 0 || last >= this.k.varnum) {
throw new IllegalArgumentException("invalid var range from " + first + " to " + last);
}
final BDDTree t = addRange(this.varTree, first, last, fixed, this.blockId, this.k.level2var);
if (t == null) {
throw new IllegalStateException("Could not add range to tree");
}
this.varTree = t;
this.blockId++;
}
/**
* Adds a single variable block for all variables known by the kernel.
*/
public void addVariableBlockAll() {
for (int n = 0; n < this.k.varnum; n++) {
addVariableBlock(n, n, false);
}
}
/**
* IMPORTANT:
* The semantics of the "level" field in the BddNode struct changes during
* variable reordering in order to make a fast variable swap possible when
* two variables are independent. Instead of referring to the level of the node
* it refers to the *variable* !!!
* @param n the variable number
* @return the level of this variable
*/
protected int var(final int n) {
return this.k.level(n);
}
protected int reorderNodenum() {
return this.k.nodesize - this.k.freenum;
}
protected int nodehashReorder(final int var, final int l, final int h) {
return Math.abs(this.k.pair(l, h) % this.levels[var].size) + this.levels[var].start;
}
protected void reorderBlock(final BDDTree t, final BDDReorderingMethod method) {
BDDTree thisTree;
if (t == null) {
return;
}
if (!t.isFixed() && t.getNextlevel() != null) {
switch (method) {
case BDD_REORDER_WIN2:
t.setNextlevel(reorderWin2(t.getNextlevel()));
break;
case BDD_REORDER_WIN2ITE:
t.setNextlevel(reorderWin2ite(t.getNextlevel()));
break;
case BDD_REORDER_SIFT:
t.setNextlevel(reorderSift(t.getNextlevel()));
break;
case BDD_REORDER_SIFTITE:
t.setNextlevel(reorderSiftite(t.getNextlevel()));
break;
case BDD_REORDER_WIN3:
t.setNextlevel(reorderWin3(t.getNextlevel()));
break;
case BDD_REORDER_WIN3ITE:
t.setNextlevel(reorderWin3ite(t.getNextlevel()));
break;
case BDD_REORDER_RANDOM:
t.setNextlevel(reorderRandom(t.getNextlevel()));
break;
}
}
for (thisTree = t.getNextlevel(); thisTree != null; thisTree = thisTree.getNext()) {
reorderBlock(thisTree, method);
}
if (t.getSeq() != null) {
// qsort(t->seq, t->last-t->first+1, sizeof(int), varseqCmp);
t.setSeq(Arrays.stream(t.getSeq()).limit(t.getLast() - t.getFirst() + 1).boxed()
.sorted(this::varseqCmp)
.mapToInt(i -> i)
.toArray());
}
}
protected int varseqCmp(final Integer aa, final Integer bb) {
final int a = this.k.var2level[aa];
final int b = this.k.var2level[bb];
return Integer.compare(a, b);
}
protected void reorderDone() {
for (int n = 0; n < this.extRootSize; n++) {
this.k.setMark(this.extRoots[n]);
}
for (int n = 2; n < this.k.nodesize; n++) {
if (this.k.marked(n)) {
this.k.unmark(n);
} else {
this.k.setRefcou(n, 0);
}
/* This is where we go from .var to .level again! - Do NOT use the LEVEL macro here. */
this.k.setLevel(n, this.k.var2level[this.k.level(n)]);
}
this.k.gbc();
}
protected BDDTree reorderWin2(final BDDTree t) {
BDDTree thisTree = t;
BDDTree first = t;
if (t == null) {
return null;
}
while (thisTree.getNext() != null) {
final int best = reorderNodenum();
blockdown(thisTree);
if (best < reorderNodenum()) {
blockdown(thisTree.getPrev());
thisTree = thisTree.getNext();
} else if (first == thisTree) {
first = thisTree.getPrev();
}
}
return first;
}
protected BDDTree reorderWin2ite(final BDDTree t) {
BDDTree thisTree;
BDDTree first = t;
if (t == null) {
return null;
}
int lastsize;
do {
lastsize = reorderNodenum();
thisTree = t;
while (thisTree.getNext() != null) {
final int best = reorderNodenum();
blockdown(thisTree);
if (best < reorderNodenum()) {
blockdown(thisTree.getPrev());
thisTree = thisTree.getNext();
} else if (first == thisTree) {
first = thisTree.getPrev();
}
}
}
while (reorderNodenum() != lastsize);
return first;
}
protected BDDTree reorderWin3(final BDDTree t) {
BDDTree thisTree = t;
BDDTree first = t;
if (t == null) {
return null;
}
while (thisTree.getNext() != null) {
final Pair swapResult = reorderSwapwin3(thisTree);
thisTree = swapResult.first();
first = swapResult.second() != null ? swapResult.second() : first;
}
return first;
}
protected BDDTree reorderWin3ite(final BDDTree t) {
BDDTree thisTree;
BDDTree first = t;
int lastsize;
if (t == null) {
return null;
}
do {
lastsize = reorderNodenum();
thisTree = first;
while (thisTree.getNext() != null && thisTree.getNext().getNext() != null) {
final Pair swapResult = reorderSwapwin3(thisTree);
thisTree = swapResult.first();
first = swapResult.second() != null ? swapResult.second() : first;
}
}
while (reorderNodenum() != lastsize);
return first;
}
protected Pair reorderSwapwin3(BDDTree thisTree) {
BDDTree first = null;
final boolean setfirst = thisTree.getPrev() == null;
BDDTree next = thisTree;
int best = reorderNodenum();
if (thisTree.getNext().getNext() == null) /* Only two blocks left -> win2 swap */ {
blockdown(thisTree);
if (best < reorderNodenum()) {
blockdown(thisTree.getPrev());
next = thisTree.getNext();
} else {
if (setfirst) {
first = thisTree.getPrev();
}
}
} else /* Real win3 swap */ {
int pos = 0;
blockdown(thisTree); /* B A* C (4) */
pos++;
if (best > reorderNodenum()) {
pos = 0;
best = reorderNodenum();
}
blockdown(thisTree); /* B C A* (3) */
pos++;
if (best > reorderNodenum()) {
pos = 0;
best = reorderNodenum();
}
thisTree = thisTree.getPrev().getPrev();
blockdown(thisTree); /* C B* A (2) */
pos++;
if (best > reorderNodenum()) {
pos = 0;
best = reorderNodenum();
}
blockdown(thisTree); /* C A B* (1) */
pos++;
if (best > reorderNodenum()) {
pos = 0;
best = reorderNodenum();
}
thisTree = thisTree.getPrev().getPrev();
blockdown(thisTree); /* A C* B (0)*/
pos++;
if (best > reorderNodenum()) {
pos = 0;
}
if (pos >= 1) /* A C B -> C A* B */ {
thisTree = thisTree.getPrev();
blockdown(thisTree);
next = thisTree;
if (setfirst) {
first = thisTree.getPrev();
}
}
if (pos >= 2) /* C A B -> C B A* */ {
blockdown(thisTree);
next = thisTree.getPrev();
if (setfirst) {
first = thisTree.getPrev().getPrev();
}
}
if (pos >= 3) /* C B A -> B C* A */ {
thisTree = thisTree.getPrev().getPrev();
blockdown(thisTree);
next = thisTree;
if (setfirst) {
first = thisTree.getPrev();
}
}
if (pos >= 4) /* B C A -> B A C* */ {
blockdown(thisTree);
next = thisTree.getPrev();
if (setfirst) {
first = thisTree.getPrev().getPrev();
}
}
if (pos >= 5) /* B A C -> A B* C */ {
thisTree = thisTree.getPrev().getPrev();
blockdown(thisTree);
next = thisTree;
if (setfirst) {
first = thisTree.getPrev();
}
}
}
return new Pair<>(next, first);
}
/**
* Do sifting iteratively until no more improvement can be found
* @param t the input BDD tree
* @return the sifted BDD tree
*/
protected BDDTree reorderSiftite(final BDDTree t) {
BDDTree first = t;
int lastsize;
if (t == null) {
return null;
}
do {
lastsize = reorderNodenum();
first = reorderSift(first);
}
while (reorderNodenum() != lastsize);
return first;
}
/**
* Find sifting sequence based on the number of nodes at each level
* @param t the input BDD tree
* @return the sifted BDD tree
*/
protected BDDTree reorderSift(BDDTree t) {
BDDTree thisTree;
final BDDTree[] seq;
final BDDSizePair[] p;
int n, num;
for (thisTree = t, num = 0; thisTree != null; thisTree = thisTree.getNext()) {
thisTree.setPos(num++);
}
p = new BDDSizePair[num];
for (int i = 0; i < p.length; i++) {
p[i] = new BDDSizePair();
}
seq = new BDDTree[num];
for (thisTree = t, n = 0; thisTree != null; thisTree = thisTree.getNext(), n++) {
int v;
/* Accumulate number of nodes for each block */
p[n].val = 0;
for (v = thisTree.getFirst(); v <= thisTree.getLast(); v++) {
p[n].val = p[n].val - this.levels[v].nodenum;
}
p[n].block = thisTree;
}
/* Sort according to the number of nodes at each level */
Arrays.sort(p, 0, num, this::siftTestCmp);
/* Create sequence */
for (n = 0; n < num; n++) {
seq[n] = p[n].block;
}
/* Do the sifting on this sequence */
t = reorderSiftSeq(t, seq, num);
return t;
}
/**
* Go through all blocks in a specific sequence and find best
* position for each of them
* @param t the input BDD tree
* @param seq the sequence
* @param num the current position in the sequence
* @return the sifted BDD tree
*/
protected BDDTree reorderSiftSeq(final BDDTree t, final BDDTree[] seq, final int num) {
BDDTree thisTree;
int n;
if (t == null) {
return null;
}
for (n = 0; n < num; n++) {
reorderSiftBestpos(seq[n], num / 2);
}
/* Find first block */
for (thisTree = t; thisTree.getPrev() != null; thisTree = thisTree.getPrev()) {
/* nil */
}
return thisTree;
}
/**
* Move a specific block up and down in the order and place at last in
* the best position
* @param blk the block
* @param middlePos the middle position in the block
*/
protected void reorderSiftBestpos(final BDDTree blk, final int middlePos) {
int best = reorderNodenum();
int maxAllowed = best / 5 + best;
int bestpos = 0;
boolean dirIsUp = true;
int n;
/* Determine initial direction */
if (blk.getPos() > middlePos) {
dirIsUp = false;
}
/* Move block back and forth */
for (n = 0; n < 2; n++) {
boolean first = true;
if (dirIsUp) {
while (blk.getPrev() != null &&
(reorderNodenum() <= maxAllowed || first)) {
first = false;
blockdown(blk.getPrev());
bestpos--;
if (reorderNodenum() < best) {
best = reorderNodenum();
bestpos = 0;
maxAllowed = best / 5 + best;
}
}
} else {
while (blk.getNext() != null &&
(reorderNodenum() <= maxAllowed || first)) {
first = false;
blockdown(blk);
bestpos++;
if (reorderNodenum() < best) {
best = reorderNodenum();
bestpos = 0;
maxAllowed = best / 5 + best;
}
}
}
dirIsUp = !dirIsUp;
}
/* Move to best pos */
while (bestpos < 0) {
blockdown(blk);
bestpos++;
}
while (bestpos > 0) {
blockdown(blk.getPrev());
bestpos--;
}
}
protected int siftTestCmp(final BDDSizePair a, final BDDSizePair b) {
return Integer.compare(a.val, b.val);
}
/*
* === Random reordering (mostly for debugging and test ) =============
*/
protected BDDTree reorderRandom(final BDDTree t) {
BDDTree thisTree;
final BDDTree[] seq;
int n, num = 0;
if (t == null) {
return null;
}
for (thisTree = t; thisTree != null; thisTree = thisTree.getNext()) {
num++;
}
seq = new BDDTree[num];
for (thisTree = t, num = 0; thisTree != null; thisTree = thisTree.getNext()) {
seq[num++] = thisTree;
}
final Random random = new Random(42);
for (n = 0; n < 4 * num; n++) {
final int blk = random.nextInt(num);
if (seq[blk].getNext() != null) {
blockdown(seq[blk]);
}
}
/* Find first block */
for (thisTree = t; thisTree.getPrev() != null; thisTree = thisTree.getPrev()) {
/* nil */
}
return thisTree;
}
/**
* Swaps adjacent blocks
* @param left the left BDD tree
*/
protected void blockdown(final BDDTree left) {
final BDDTree right = left.getNext();
int n;
final int leftsize = left.getLast() - left.getFirst();
final int rightsize = right.getLast() - right.getFirst();
final int leftstart = this.k.var2level[left.getSeq()[0]];
final int[] lseq = left.getSeq();
final int[] rseq = right.getSeq();
/* Move left past right */
while (this.k.var2level[lseq[0]] < this.k.var2level[rseq[rightsize]]) {
for (n = 0; n < leftsize; n++) {
if (this.k.var2level[lseq[n]] + 1 != this.k.var2level[lseq[n + 1]] && this.k.var2level[lseq[n]] < this.k.var2level[rseq[rightsize]]) {
reorderVardown(lseq[n]);
}
}
if (this.k.var2level[lseq[leftsize]] < this.k.var2level[rseq[rightsize]]) {
reorderVardown(lseq[leftsize]);
}
}
/* Move right to where left started */
while (this.k.var2level[rseq[0]] > leftstart) {
for (n = rightsize; n > 0; n--) {
if (this.k.var2level[rseq[n]] - 1 != this.k.var2level[rseq[n - 1]] && this.k.var2level[rseq[n]] > leftstart) {
reorderVarup(rseq[n]);
}
}
if (this.k.var2level[rseq[0]] > leftstart) {
reorderVarup(rseq[0]);
}
}
/* Swap left and right data in the order */
left.setNext(right.getNext());
right.setPrev(left.getPrev());
left.setPrev(right);
right.setNext(left);
if (right.getPrev() != null) {
right.getPrev().setNext(right);
}
if (left.getNext() != null) {
left.getNext().setPrev(left);
}
n = left.getPos();
left.setPos(right.getPos());
right.setPos(n);
}
protected void reorderVarup(final int var) {
if (var < 0 || var >= this.k.varnum) {
throw new IllegalStateException("Illegal variable in reordering");
}
if (this.k.var2level[var] != 0) {
reorderVardown(this.k.level2var[this.k.var2level[var] - 1]);
}
}
protected void reorderVardown(final int var) {
int n;
final int level;
if (var < 0 || var >= this.k.varnum) {
throw new IllegalStateException("Illegal variable in reordering");
}
level = this.k.var2level[var];
if (level >= this.k.varnum - 1) {
return;
}
this.resizedInMakenode = false;
if (this.interactionMatrix.depends(var, this.k.level2var[level + 1]) > 0) {
final int toBeProcessed = reorderDownSimple(var);
reorderSwap(toBeProcessed, var);
reorderLocalGbc(var);
}
/* Swap the var<->level tables */
n = this.k.level2var[level];
this.k.level2var[level] = this.k.level2var[level + 1];
this.k.level2var[level + 1] = n;
n = this.k.var2level[var];
this.k.var2level[var] = this.k.var2level[this.k.level2var[level]];
this.k.var2level[this.k.level2var[level]] = n;
/* Update all rename pairs */
// this.pairs.vardown(level);
if (this.resizedInMakenode) {
reorderRehashAll();
}
}
protected int reorderDownSimple(final int var0) {
int toBeProcessed = 0;
final int var1 = this.k.level2var[this.k.var2level[var0] + 1];
final int vl0 = this.levels[var0].start;
final int size0 = this.levels[var0].size;
int n;
this.levels[var0].nodenum = 0;
for (n = 0; n < size0; n++) {
int r;
r = this.k.hash(n + vl0);
this.k.setHash(n + vl0, 0);
while (r != 0) {
final int next = this.k.next(r);
if (var(this.k.low(r)) != var1 && var(this.k.high(r)) != var1) {
/* Node does not depend on next var, let it stay in the chain */
this.k.setNext(r, this.k.hash(n + vl0));
this.k.setHash(n + vl0, r);
this.levels[var0].nodenum++;
} else {
/* Node depends on next var - save it for later procesing */
this.k.setNext(r, toBeProcessed);
toBeProcessed = r;
}
r = next;
}
}
return toBeProcessed;
}
protected void reorderSwap(int toBeProcessed, final int var0) {
final int var1 = this.k.level2var[this.k.var2level[var0] + 1];
while (toBeProcessed > 0) {
final int next = this.k.next(toBeProcessed);
int f0 = this.k.low(toBeProcessed);
int f1 = this.k.high(toBeProcessed);
final int f00;
final int f01;
final int f10;
final int f11;
final int hash;
/* Find the cofactors for the new nodes */
if (var(f0) == var1) {
f00 = this.k.low(f0);
f01 = this.k.high(f0);
} else {
f00 = f01 = f0;
}
if (var(f1) == var1) {
f10 = this.k.low(f1);
f11 = this.k.high(f1);
} else {
f10 = f11 = f1;
}
/* Note: makenode does refcou. */
f0 = reorderMakenode(var0, f00, f10);
f1 = reorderMakenode(var0, f01, f11);
// assert node == this.nodes[toBeProcessed];
// node = this.nodes[toBeProcessed]; /* Might change in makenode [SHi: why? I don't think so] */
/* We know that the refcou of the grandchilds of this node
* is greater than one (these are f00...f11), so there is
* no need to do a recursive refcou decrease. It is also
* possible for the LOWp(node)/high nodes to come alive again,
* so deref. of the childs is delayed until the local GBC. */
this.k.decRef(this.k.low(toBeProcessed));
this.k.decRef(this.k.high(toBeProcessed));
/* Update in-place */
this.k.setLevel(toBeProcessed, var1);
this.k.setLow(toBeProcessed, f0);
this.k.setHigh(toBeProcessed, f1);
this.levels[var1].nodenum++;
/* Rehash the node since it got new childs */
hash = this.nodehashReorder(var(toBeProcessed), this.k.low(toBeProcessed), this.k.high(toBeProcessed));
this.k.setNext(toBeProcessed, this.k.hash(hash));
this.k.setHash(hash, toBeProcessed);
toBeProcessed = next;
}
}
protected int reorderMakenode(final int var, final int low, final int high) {
final int hash;
int res;
/* Note: We know that low,high has a refcou greater than zero, so there is no need to add reference *recursively* */
/* check whether childs are equal */
if (low == high) {
this.k.incRef(low);
return low;
}
/* Try to find an existing node of this kind */
hash = this.nodehashReorder(var, low, high);
res = this.k.hash(hash);
while (res != 0) {
if (this.k.low(res) == low && this.k.high(res) == high) {
this.k.incRef(res);
return res;
}
res = this.k.next(res);
}
/* No existing node -> build one */
/* Any free nodes to use ? */
if (this.k.freepos == 0) {
/* Try to allocate more nodes - call noderesize without
* enabling rehashing.
* Note: if ever rehashing is allowed here, then remember to
* update local variable "hash" */
this.k.nodeResize(false);
this.resizedInMakenode = true;
assert this.k.freepos > 0;
}
/* Build new node */
res = this.k.freepos;
this.k.freepos = this.k.next(this.k.freepos);
this.levels[var].nodenum++;
this.k.produced++;
this.k.freenum--;
this.k.setLevel(res, var);
this.k.setLow(res, low);
this.k.setHigh(res, high);
/* Insert node in hash chain */
this.k.setNext(res, this.k.hash(hash));
this.k.setHash(hash, res);
/* Make sure it is reference counted */
this.k.setRefcou(res, 1);
this.k.incRef(this.k.low(res));
this.k.incRef(this.k.high(res));
return res;
}
protected void reorderLocalGbc(final int var0) {
final int var1 = this.k.level2var[this.k.var2level[var0] + 1];
final int vl1 = this.levels[var1].start;
final int size1 = this.levels[var1].size;
int n;
for (n = 0; n < size1; n++) {
final int hash = n + vl1;
int r = this.k.hash(hash);
this.k.setHash(hash, 0);
while (r > 0) {
final int next = this.k.next(r);
if (this.k.refcou(r) > 0) {
this.k.setNext(r, this.k.hash(hash));
this.k.setHash(hash, r);
} else {
this.k.decRef(this.k.low(r));
this.k.decRef(this.k.high(r));
this.k.setLow(r, -1);
this.k.setNext(r, this.k.freepos);
this.k.freepos = r;
this.levels[var1].nodenum--;
this.k.freenum++;
}
r = next;
}
}
}
protected void reorderRehashAll() {
int n;
reorderSetLevellookup();
this.k.freepos = 0;
for (n = this.k.nodesize - 1; n >= 0; n--) {
this.k.setHash(n, 0);
}
for (n = this.k.nodesize - 1; n >= 2; n--) {
if (this.k.refcou(n) > 0) {
final int hash = this.nodehashReorder(var(n), this.k.low(n), this.k.high(n));
this.k.setNext(n, this.k.hash(hash));
this.k.setHash(hash, n);
} else {
this.k.setNext(n, this.k.freepos);
this.k.freepos = n;
}
}
}
protected void reorderSetLevellookup() {
int n;
for (n = 0; n < this.k.varnum; n++) {
this.levels[n].maxsize = this.k.nodesize / this.k.varnum;
this.levels[n].start = n * this.levels[n].maxsize;
this.levels[n].size = this.levels[n].maxsize;
if (this.levels[n].size >= 4) {
this.levels[n].size = this.k.getPrime().primeLTE(this.levels[n].size);
}
}
}
protected void clrVarBlocks() {
this.varTree = null;
this.blockId = 0;
}
protected void disableReorder() {
this.reorderDisabled = true;
}
protected void enableReorder() {
this.reorderDisabled = false;
}
protected boolean reorderReady() {
return this.reorderMethod != BDDReorderingMethod.BDD_REORDER_NONE && this.varTree != null && this.bddreorderTimes != 0 && !this.reorderDisabled;
}
protected void reorderAuto() {
if (!reorderReady()) {
return;
}
reorder(this.reorderMethod);
this.bddreorderTimes--;
}
protected int reorderInit() {
this.levels = new LevelData[this.k.varnum];
for (int n = 0; n < this.k.varnum; n++) {
this.levels[n] = new LevelData();
this.levels[n].start = -1;
this.levels[n].size = 0;
this.levels[n].nodenum = 0;
}
/* First mark and recursive refcou. all roots and childs. Also do some
* setup here for both setLevellookup and reorder_gbc */
if (markRoots() < 0) {
return -1;
}
/* Initialize the hash tables */
reorderSetLevellookup();
/* Garbage collect and rehash to new scheme */
reorderGbc();
return 0;
}
protected int markRoots() {
final int[] dep = new int[this.k.varnum];
this.extRootSize = 0;
for (int n = 2; n < this.k.nodesize; n++) {
/* This is where we go from .level to .var! - Do NOT use the LEVEL macro here. */
this.k.setLevel(n, this.k.level2var[this.k.level(n)]);
if (this.k.refcou(n) > 0) {
this.extRootSize++;
this.k.setMark(n);
}
}
this.extRoots = new int[this.extRootSize];
this.interactionMatrix = new InteractionMatrix(this.k.varnum);
this.extRootSize = 0;
for (int n = 2; n < this.k.nodesize; n++) {
if (this.k.marked(n)) {
this.k.unmarkNode(n);
this.extRoots[this.extRootSize++] = n;
dep[var(n)] = 1;
this.levels[var(n)].nodenum++;
addrefRec(this.k.low(n), dep);
addrefRec(this.k.high(n), dep);
addDependencies(dep);
}
/* Make sure the hash field is empty. This saves a loop in the initial GBC */
this.k.setHash(n, 0);
}
this.k.setHash(0, 0);
this.k.setHash(1, 0);
return 0;
}
protected void reorderGbc() {
this.k.freepos = 0;
this.k.freenum = 0;
/* No need to zero all hash fields - this is done in mark_roots */
for (int n = this.k.nodesize - 1; n >= 2; n--) {
if (this.k.refcou(n) > 0) {
final int hash = nodehashReorder(var(n), this.k.low(n), this.k.high(n));
this.k.setNext(n, this.k.hash(hash));
this.k.setHash(hash, n);
} else {
this.k.setLow(n, -1);
this.k.setNext(n, this.k.freepos);
this.k.freepos = n;
this.k.freenum++;
}
}
}
protected void checkReorder() {
reorderAuto();
/* Do not reorder before twice as many nodes have been used */
this.usedNodesNextReorder = 2 * (this.k.nodesize - this.k.freenum);
/* And if very little was gained this time (< 20%) then wait until
* even more nodes (upto twice as many again) have been used */
if (reorderGain() < 20) {
this.usedNodesNextReorder += (this.usedNodesNextReorder * (20 - reorderGain())) / 20;
}
}
protected void addrefRec(final int r, final int[] dep) {
if (r < 2) {
return;
}
if (this.k.refcou(r) == 0) {
this.k.freenum--;
/* Detect variable dependencies for the interaction matrix */
dep[var(r) & MARKHIDE] = 1;
/* Make sure the nodenum field is updated. Used in the initial GBC */
this.levels[var(r) & MARKHIDE].nodenum++;
addrefRec(this.k.low(r), dep);
addrefRec(this.k.high(r), dep);
} else {
int n;
/* Update (from previously found) variable dependencies
* for the interaction matrix */
for (n = 0; n < this.k.varnum; n++) {
dep[n] |= this.interactionMatrix.depends(var(r) & MARKHIDE, n);
}
}
this.k.incRef(r);
}
protected void addDependencies(final int[] dep) {
for (int n = 0; n < this.k.varnum; n++) {
for (int m = n; m < this.k.varnum; m++) {
if (dep[n] > 0 && dep[m] > 0) {
this.interactionMatrix.set(n, m);
this.interactionMatrix.set(m, n);
}
}
}
}
protected int reorderGain() {
if (this.usednumBefore == 0) {
return 0;
}
return (100 * (this.usednumBefore - this.usednumAfter)) / this.usednumBefore;
}
/* Level data */
protected static class LevelData {
protected int start; /* Start of this sub-table (entry in "bddnodes") */
protected int size; /* Size of this sub-table */
protected int maxsize; /* Max. allowed size of sub-table */
protected int nodenum; /* Number of nodes in this level */
}
protected static class BDDSizePair {
protected int val;
protected BDDTree block;
}
protected static class InteractionMatrix {
protected final int[][] rows;
protected InteractionMatrix(final int size) {
this.rows = new int[size][];
for (int n = 0; n < size; n++) {
this.rows[n] = new int[size / 8 + 1];
}
}
protected void set(final int a, final int b) {
this.rows[a][b / 8] |= 1 << (b % 8);
}
protected int depends(final int a, final int b) {
return this.rows[a][b / 8] & (1 << (b % 8));
}
}
}