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.
 
 
 
 
 
 

266 lines
7.6 KiB

//==============================================================================
//
// Copyright (c) 2002-
// Authors:
// * Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford, formerly University of Birmingham)
// * Joachim Klein <klein@tcs.inf.tu-dresden.de> (TU Dresden)
//
//------------------------------------------------------------------------------
//
// 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 "odd.h"
// static variables
static int num_odd_nodes = 0;
// local prototypes
static ODDNode *build_odd_rec(DdManager *ddman, DdNode *dd, int level, DdNode **vars, int num_vars, ODDNode **tables);
static int64_t add_offsets(DdManager *ddman, ODDNode *dd, int level, int num_vars);
static DdNode *single_index_to_bdd_rec(DdManager *ddman, int i, DdNode **vars, int num_vars, int level, ODDNode *odd, long o);
//------------------------------------------------------------------------------
ODDNode *build_odd(DdManager *ddman, DdNode *dd, DdNode **vars, int num_vars)
{
int i;
ODDNode **tables;
ODDNode *res;
// build tables to store odd nodes during construction
tables = new ODDNode*[num_vars+1];
for (i = 0; i < num_vars+1; i++) {
tables[i] = NULL;
}
// reset node counter
num_odd_nodes = 0;
// call recursive bit
res = build_odd_rec(ddman, dd, 0, vars, num_vars, tables);
// At this point, all the allocated ODDNodes for this ODD are
// chained by linked lists (ODD->next), one for each non-empty tables[i].
// To facilitate deallocation later on, we chain all these
// individual linked lists together.
// By construction, the root node (res) is the only node in
// the top-most, non-empty table and is thus at the
// start of the resulting chain.
// The current end of the linked list
ODDNode *last = NULL;
for (i = 0; i < num_vars+1; i++) {
if (tables[i] != NULL) {
// non-empty tables slot
if (last != NULL) {
// chain the first node in this tables slot to
// the last one above
last->next = tables[i];
}
// search for last node in this tables slot
last = tables[i];
while (last->next != NULL) {
last = last->next;
}
}
}
// add offsets to odd
if (add_offsets(ddman, res, 0, num_vars) < 0) {
// negative value indicates that there was an arithmetic overflow
// cleanup and return 0
clear_odd(res);
res = 0;
}
// free memory
delete tables;
return res;
}
//------------------------------------------------------------------------------
static ODDNode *build_odd_rec(DdManager *ddman, DdNode *dd, int level, DdNode **vars, int num_vars, ODDNode **tables)
{
ODDNode *ptr;
// see if we already have odd in the tables
ptr = tables[level];
while (ptr != NULL) {
if (ptr->dd == dd) break;
ptr = ptr->next;
}
// if not, add it
if (ptr == NULL) {
num_odd_nodes++;
ptr = new ODDNode();
ptr->dd = dd;
ptr->next = tables[level];
tables[level] = ptr;
// and recurse...
// should be able to add this because will never traverse a path to the
// zeros temrinal - always look at states that exist
// can we assume this?
// if (dd == Cudd_ReadZero(ddman)) return;
if (level == num_vars) {
ptr->e = NULL;
ptr->t = NULL;
}
else if (vars[level]->index < dd->index) {
ptr->e = build_odd_rec(ddman, dd, level+1, vars, num_vars, tables);
ptr->t = ptr->e;
}
else {
ptr->e = build_odd_rec(ddman, Cudd_E(dd), level+1, vars, num_vars, tables);
ptr->t = build_odd_rec(ddman, Cudd_T(dd), level+1, vars, num_vars, tables);
}
ptr->eoff = -1;
ptr->toff = -1;
}
return ptr;
}
//------------------------------------------------------------------------------
//
// Compute the actual eoff and toff values.
// Returns eoff + toff for this odd node or -1 if there is an arithmetic overflow
// (can not store eoff+toff in an int64_t)
int64_t add_offsets(DdManager *ddman, ODDNode *odd, int level, int num_vars)
{
if ((odd->eoff == -1) || (odd->toff == -1)) {
// this node has not yet been seen
if (level == num_vars) {
if (odd->dd == Cudd_ReadZero(ddman)) {
odd->eoff = 0;
odd->toff = 0;
}
else {
odd->eoff = 0;
odd->toff = 1;
}
}
else {
odd->eoff = add_offsets(ddman, odd->e, level+1, num_vars);
if (odd->eoff < 0) return -1;
odd->toff = add_offsets(ddman, odd->t, level+1, num_vars);
if (odd->toff < 0) return -1;
}
// overflow check for sum
// do unsigned addition, guaranteed to not overflow
// as eoff and toff are signed and positive, cast sum to signed and
// check that it is larger than one of the summands
int64_t tmp = (int64_t)((uint64_t)odd->eoff + (uint64_t)odd->toff);
if (tmp < odd->eoff) {
// we have an overflow
return -1;
}
}
return odd->eoff + odd->toff;
}
//------------------------------------------------------------------------------
void clear_odd(ODDNode *odd) {
// We assume that odd is the root node of an ODD, i.e.,
// was returned previously from a build_odd call.
// It is thus the first element of the linked list
// (with ODDNode->next pointers) that references all the
// allocated ODDNodes of this ODD and we can simply
// delete each ODDNode in turn.
while (odd != NULL) {
ODDNode *next = odd->next;
delete odd;
odd = next;
}
}
//------------------------------------------------------------------------------
// Get the index (according to an ODD) of the first non-zero enetry of a BDD
int get_index_of_first_from_bdd(DdManager *ddman, DdNode *dd, DdNode **vars, int num_vars, ODDNode *odd)
{
DdNode *ptr, *ptr_next;;
ODDNode *odd_ptr;
int i, index;
// Go down dd along first non-zero path
index = 0;
ptr = dd;
odd_ptr = odd;
for (i = 0; i < num_vars; i++) {
ptr_next = (ptr->index > vars[i]->index) ? ptr : Cudd_E(ptr);
if (ptr_next != Cudd_ReadZero(ddman)) {
odd_ptr = odd_ptr->e;
}
else {
ptr_next = (ptr->index > vars[i]->index) ? ptr : Cudd_T(ptr);
index += odd_ptr->eoff;
odd_ptr = odd_ptr->t;
}
ptr = ptr_next;
}
return index;
}
// Get a BDD for a single state given its index and the accompanying ODD.
EXPORT DdNode *single_index_to_bdd(DdManager *ddman, int i, DdNode **vars, int num_vars, ODDNode *odd)
{
return single_index_to_bdd_rec(ddman, i, vars, num_vars, 0, odd, 0);
}
DdNode *single_index_to_bdd_rec(DdManager *ddman, int i, DdNode **vars, int num_vars, int level, ODDNode *odd, long o)
{
DdNode *dd;
if (level == num_vars) {
return DD_Constant(ddman, 1);
}
else {
if (odd->eoff > i - o) {
dd = single_index_to_bdd_rec(ddman, i, vars, num_vars, level+1, odd->e, o);
Cudd_Ref(vars[level]);
return DD_And(ddman, DD_Not(ddman, vars[level]), dd);
}
else {
dd = single_index_to_bdd_rec(ddman, i, vars, num_vars, level+1, odd->t, o+odd->eoff);
Cudd_Ref(vars[level]);
return DD_And(ddman, vars[level], dd);
}
}
}
//------------------------------------------------------------------------------
int get_num_odd_nodes()
{
return num_odd_nodes;
}
//------------------------------------------------------------------------------