You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

439 lines
13 KiB

//==============================================================================
//
// Copyright (c) 2002-
// Authors:
// * Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford, formerly University of Birmingham)
// Description: CUDD functions
//
//------------------------------------------------------------------------------
//
// This file is part of PRISM.
//
// PRISM is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// PRISM is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with PRISM; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//==============================================================================
#include "util.h"
#include "cuddInt.h"
#include "dd_cudd.h"
#include <map>
#include <set>
extern FILE *dd_out;
// A flag indicating that a CUDD error has occurred
// that could not be signalled by returning a NULL DdNode*
// from a function
bool dd_cudd_error_flag = false;
static int Cudd_CheckZeroRefVerbose(DdManager *ddman);
//-----------------------------------------------------------------------------------
DdManager *DD_InitialiseCUDD()
{
// choose some ensible defaults
// (cudd max memory = 200 MB)
// (cudd epsilon = 1.0e-15, very close to min precision of doubles, 1.1e-16)
return DD_InitialiseCUDD(200*1024, 1.0e-15);
}
//-----------------------------------------------------------------------------------
DdManager *DD_InitialiseCUDD(long max_mem, double epsilon)
{
DdManager *ddman;
// initialise CUDD package
ddman = Cudd_Init(0, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, max_mem * 1024);
Cudd_SetStderr(ddman, stdout);
Cudd_SetMaxMemory(ddman, max_mem * 1024);
Cudd_SetEpsilon(ddman, epsilon);
return ddman;
}
//-----------------------------------------------------------------------------------
void DD_SetCUDDMaxMem(DdManager *ddman, long max_mem)
{
Cudd_SetMaxMemory(ddman, max_mem * 1024);
}
//-----------------------------------------------------------------------------------
void DD_SetCUDDEpsilon(DdManager *ddman, double epsilon)
{
Cudd_SetEpsilon(ddman, epsilon);
}
//-----------------------------------------------------------------------------------
void DD_PrintCacheInfo(DdManager *ddman)
{
static double old_lookups, old_hits;
double slots = Cudd_ReadCacheSlots(ddman);
double used_slots = Cudd_ReadCacheUsedSlots(ddman);
double lookups = Cudd_ReadCacheLookUps(ddman);
double hits = Cudd_ReadCacheHits(ddman);
double percent;
fprintf(dd_out, "Cache info: %.2f%% of %.0f slots used, ", 100.0*used_slots, slots);
percent = (lookups <= old_lookups) ? 0 : 100.0*(hits-old_hits)/(lookups-old_lookups);
fprintf(dd_out, "lookup success: %.0f/%.0f = %.2f%% ", hits-old_hits, lookups-old_lookups, percent);
percent = (lookups <= 0) ? 0 : 100.0*hits/lookups;
fprintf(dd_out, "(total %.0f/%.0f = %.2f%%)\n", hits, lookups, 100.0*hits/lookups);
old_lookups = lookups;
old_hits = hits;
}
//-----------------------------------------------------------------------------------
void DD_CloseDownCUDD(DdManager *ddman) { DD_CloseDownCUDD(ddman, true); }
void DD_CloseDownCUDD(DdManager *ddman, bool check)
{
// fprintf(dd_out, "Memory in use: %.1fKB\n", Cudd_ReadMemoryInUse(ddman)/1024.0);
// fprintf(dd_out, "Peak number of nodes: %ld\n", Cudd_ReadPeakNodeCount(ddman));
// fprintf(dd_out, "Peak number of live nodes: %d\n", Cudd_ReadPeakLiveNodeCount(ddman));
// fprintf(dd_out, "Number of cache entries: %u\n", ddman->cacheSlots);
// fprintf(dd_out, "Hard limit for cache size: %u\n", Cudd_ReadMaxCacheHard(ddman));
// fprintf(dd_out, "Soft limit for cache size: %u\n", Cudd_ReadMaxCache(ddman));
if (check) {
// if required, check everthing is closed down OK and warn if not
// for now, we disable the debug check since there are increasingly
// problems occurring on 64 bit Linux/Mac
/*if (Cudd_DebugCheck(ddman)) {
printf("\nWarning: CUDD reports an error on closing.\n");
}*/
if (Cudd_CheckZeroRef(ddman) > 0) {
fprintf(dd_out, "\nWarning: CUDD reports %d non-zero references.\n", Cudd_CheckZeroRef(ddman));
}
}
// close down the CUDD package
Cudd_Quit(ddman);
}
//-----------------------------------------------------------------------------------
int Cudd_CheckZeroRefVerbose(DdManager *manager)
{
int size;
int i, j;
int remain;
DdNode **nodelist;
DdNode *node;
DdNode *sentinel = &(manager->sentinel);
DdSubtable *subtable;
int count = 0;
int index;
printf("Checking for non-zero references...\n");
/* First look at the BDD/ADD subtables. */
remain = 1; /* reference from the manager */
size = manager->size;
remain += 2 * size; /* reference from the BDD projection functions */
for (i = 0; i < size; i++) {
subtable = &(manager->subtables[i]);
nodelist = subtable->nodelist;
for (j = 0; (unsigned) j < subtable->slots; j++) {
node = nodelist[j];
while (node != sentinel) {
if (node->ref != 0 && node->ref != DD_MAXREF) {
index = (int) node->index;
if (node != manager->vars[index]) {
printf("* node found (index %d)\n", index);
count++;
}
else {
if (node->ref != 1) {
printf("* variable found (index %d)\n", index);
count++;
}
}
}
node = node->next;
}
}
}
/* Then look at the ZDD subtables. */
size = manager->sizeZ;
if (size) /* references from ZDD universe */
remain += 2;
for (i = 0; i < size; i++) {
subtable = &(manager->subtableZ[i]);
nodelist = subtable->nodelist;
for (j = 0; (unsigned) j < subtable->slots; j++) {
node = nodelist[j];
while (node != NULL) {
if (node->ref != 0 && node->ref != DD_MAXREF) {
index = (int) node->index;
if (node == manager->univ[manager->permZ[index]]) {
if (node->ref > 2) {
count++;
}
} else {
count++;
}
}
node = node->next;
}
}
}
/* Now examine the constant table. Plusinfinity, minusinfinity, and
** zero are referenced by the manager. One is referenced by the
** manager, by the ZDD universe, and by all projection functions.
** All other nodes should have no references.
*/
nodelist = manager->constants.nodelist;
for (j = 0; (unsigned) j < manager->constants.slots; j++) {
node = nodelist[j];
while (node != NULL) {
if (node->ref != 0 && node->ref != DD_MAXREF) {
if (node == manager->one) {
if ((int) node->ref != remain) {
count++;
}
} else if (node == manager->zero ||
node == manager->plusinfinity ||
node == manager->minusinfinity) {
if (node->ref != 1) {
count++;
}
} else {
printf("* constant found (index %g)\n", node->type.value);
count++;
}
}
node = node->next;
}
}
return(count);
}
//-----------------------------------------------------------------------------------
// -------------- Reference analysis ------------------------------------------------
// dump info about a node
static void dump_node(DdNode* node)
{
if (Cudd_IsConstant(node)) {
printf("%p: value=%f, refs=%d\n", node, Cudd_V(node), node->ref);
} else {
int index = node->index;
printf("%p: var=%d, refs=%d\n", node, index, node->ref);
}
}
// add a reference for node to the reference map
static void add_reference(std::map<DdNode*, int>& references, DdNode* node, int increase=1)
{
std::map<DdNode*, int>::iterator it = references.find(node);
if (it == references.end()) {
// node was not yet registered
references[node] = increase;
} else {
// increase the value
int& v = it->second;
v += increase;
}
}
// Analyze the nodes, return all encountered nodes in the set nodes
// and return the number of internal references for each node in the
// map internal_refs.
// An internal reference is a reference via the then or else pointer of
// another node or a reference by the manager (projection functions, some constants).
static void DD_AnalyzeRefCounts(DdManager *manager, std::set<DdNode*>& nodes, std::map<DdNode*,int>& internal_refs)
{
int size;
int i, j;
int remain;
DdNode **nodelist;
DdNode *node;
DdNode *sentinel = &(manager->sentinel);
DdSubtable *subtable;
int count = 0;
int index;
const bool debug = false;
#ifndef DD_NO_DEATH_ROW
cuddClearDeathRow(manager);
#endif
if (manager->sizeZ > 0) {
printf("Can not handle ZDD in Cudd, abort...");
return;
}
size = manager->size;
if (debug)
printf("manager->size = %d\n", size);
for (i = 0; i < size; i++) {
subtable = &(manager->subtables[i]);
nodelist = subtable->nodelist;
for (j = 0; (unsigned) j < subtable->slots; j++) {
node = nodelist[j];
while (node != sentinel) {
nodes.insert(node);
if (debug) printf("add:\n ");
if (debug) dump_node(node);
if (node->ref == DD_MAXREF) {
// TODO: Error handling, how do we deal with this case?
}
index = (int) node->index;
if (node == manager->vars[index]) {
// a projection function, deal with references from the manager later
if (debug) printf("%p is projection\n", node);
} else if (node->ref > 0) {
DdNode *t = Cudd_Regular(Cudd_T(node));
add_reference(internal_refs, t);
if (debug) printf("t ref: %p -> %d\n", t, internal_refs[t]);
DdNode *e = Cudd_Regular(Cudd_E(node));
add_reference(internal_refs, e);
if (debug) printf("e ref: %p -> %d\n", e, internal_refs[e]);
}
node = node->next;
}
}
}
for (i=0; i<size; i++) {
node = manager->vars[i];
add_reference(internal_refs, node);
if (debug) printf("::%p projection -> %d\n", node, internal_refs[node]);
DdNode *t = Cudd_Regular(Cudd_T(node));
add_reference(internal_refs, t);
if (debug) printf("t ref: %p -> %d\n", t, internal_refs[t]);
DdNode *e = Cudd_Regular(Cudd_E(node));
add_reference(internal_refs, e);
if (debug) printf("e ref: %p -> %d\n", e, internal_refs[e]);
}
// Examine the constant table. Plusinfinity, minusinfinity, one and
// zero are referenced by the manager.
nodelist = manager->constants.nodelist;
for (j = 0; (unsigned) j < manager->constants.slots; j++) {
node = nodelist[j];
while (node != NULL) {
nodes.insert(node);
if (node->ref == DD_MAXREF) {
// Error handling
}
if (node == manager->one ||
node == manager->zero ||
node == manager->plusinfinity ||
node == manager->minusinfinity) {
// manager refs
add_reference(internal_refs, node);
if (debug) printf("%p Constant(%f) -> %d\n", node, Cudd_V(node), internal_refs[node]);
}
node = node->next;
}
}
}
// Print a report about the nodes of this manager, with the number of internal references
void DD_ReportExternalRefCounts(DdManager *manager)
{
std::set<DdNode*> nodes;
std::map<DdNode*, int> internal_refs;
DD_AnalyzeRefCounts(manager, nodes, internal_refs);
printf("%lu nodes\n", nodes.size());
for (std::set<DdNode*>::iterator it = nodes.begin();
it != nodes.end();
++it) {
DdNode* node = *it;
printf("%d -> ", (internal_refs.find(node) != internal_refs.end() ? internal_refs[node] : 0));
dump_node(node);
}
// Analysis
printf("\nExternal references:\n");
for (std::set<DdNode*>::iterator it = nodes.begin();
it != nodes.end();
++it) {
DdNode* node = *it;
int internal = (internal_refs.find(node) != internal_refs.end() ? internal_refs[node] : 0);
if (node->ref > internal) {
dump_node(node);
printf(" Internal references: %d\n", internal);
} else if (node->ref < internal) {
dump_node(node);
printf("Underflow! Internal references: %d\n", internal);
}
}
}
// Analyze the BDD and return the implied number of external references per node
// in the map external_refs (only return nodes with non-zero external references)
void DD_GetExternalRefCounts(DdManager *manager, std::map<DdNode*,int>& external_refs)
{
std::set<DdNode*> nodes;
std::map<DdNode*, int> internal_refs;
DD_AnalyzeRefCounts(manager, nodes, internal_refs);
for (std::set<DdNode*>::iterator it = nodes.begin();
it != nodes.end();
++it) {
DdNode* node = *it;
int internal = (internal_refs.find(node) != internal_refs.end() ? internal_refs[node] : 0);
if (node->ref != internal) {
external_refs[node] = node->ref - internal;
}
}
// printf("Found %lu problematic nodes\n", external_refs.size());
}
//-----------------------------------------------------------------------------------
// Get the value of the DD error flag
bool DD_GetErrorFlag(DdManager *ddman)
{
return dd_cudd_error_flag || (ddman->errorCode != CUDD_NO_ERROR);
}
//-----------------------------------------------------------------------------------
// Set the DD error flag. Should be set if a CUDD error has been
// detected that could not be signalled by returning a NULL DdNode*
// from the function
void DD_SetErrorFlag()
{
dd_cudd_error_flag = true;
}
//-----------------------------------------------------------------------------------