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.
1738 lines
54 KiB
1738 lines
54 KiB
//==============================================================================
|
|
//
|
|
// Copyright (c) 2002-
|
|
// Authors:
|
|
// * Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford, formerly University of Birmingham)
|
|
// * Rashid Mehmood <rxm@cs.bham.ac.uk> (University of Birmingham)
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
// 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 <dv.h>
|
|
#include "sparse.h"
|
|
#include "hybrid.h"
|
|
#include "PrismHybrid.h"
|
|
#include "PrismHybridGlob.h"
|
|
#include <cmath>
|
|
#include <new>
|
|
|
|
// globals (used by local functions)
|
|
static HDDMatrix *hddm;
|
|
static HDDNode *zero;
|
|
static int *starts;
|
|
static RMSparseMatrix *rmsm;
|
|
static CMSparseMatrix *cmsm;
|
|
static CMSRSparseMatrix *cmsrsm;
|
|
static CMSCSparseMatrix *cmscsm;
|
|
static int num_levels;
|
|
static bool row_major;
|
|
static bool compact_sm;
|
|
static double *sm_dist;
|
|
static int sm_dist_shift;
|
|
static int sm_dist_mask;
|
|
|
|
// local prototypes
|
|
static HDDNode *build_hdd_matrix_rowrec(DdNode *dd, DdNode **rvars, DdNode **cvars, int num_vars, int level, ODDNode *row, ODDNode *col);
|
|
static HDDNode *build_hdd_matrix_colrec(DdNode *dd, DdNode **rvars, DdNode **cvars, int num_vars, int level, ODDNode *row, ODDNode *col);
|
|
static void traverse_hdd_rec(HDDNode *hdd, int level, int stop, int r, int c, int code, bool transpose);
|
|
static void traverse_odd_rec(ODDNode *odd, int level, int stop, int index, int code);
|
|
static int compute_n_and_nnz_rec(HDDNode *hdd, int level, int num_levels, ODDNode *row, ODDNode *col, bool transpose);
|
|
static RMSparseMatrix *build_rm_sparse_matrix(HDDNode *hdd, int level, bool transpose);
|
|
static CMSparseMatrix *build_cm_sparse_matrix(HDDNode *hdd, int level, bool transpose);
|
|
static CMSRSparseMatrix *build_cmsr_sparse_matrix(HDDNode *hdd, int level, bool transpose);
|
|
static CMSCSparseMatrix *build_cmsc_sparse_matrix(HDDNode *hdd, int level, bool transpose);
|
|
static void build_mdp_cubes_rec(DdNode *dd, DdNode *cube, DdNode **rvars, DdNode **cvars, int num_vars, DdNode **ndvars, int num_ndvars, int level, ODDNode *odd, HDDMatrices *hddms);
|
|
static void hdd_negative_row_sums_rec(HDDNode *hdd, int level, int row_offset, int col_offset, double *diags, bool transpose);
|
|
static void hdd_negative_row_sums_rm(RMSparseMatrix *rmsm, int row_offset, int col_offset, double *diags, bool transpose);
|
|
static void hdd_negative_row_sums_cm(CMSparseMatrix *cmsm, int row_offset, int col_offset, double *diags, bool transpose);
|
|
static void hdd_negative_row_sums_cmsr(CMSRSparseMatrix *cmsrsm, int row_offset, int col_offset, double *diags, bool transpose);
|
|
static void hdd_negative_row_sums_cmsc(CMSCSparseMatrix *cmscsm, int row_offset, int col_offset, double *diags, bool transpose);
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Data structure constructors/deconstructors
|
|
//------------------------------------------------------------------------------
|
|
|
|
HDDMatrix::HDDMatrix()
|
|
{
|
|
num_levels = 0;
|
|
// make sure pointers are init'ed to null
|
|
row_lists = NULL;
|
|
col_lists = NULL;
|
|
row_tables = NULL;
|
|
col_tables = NULL;
|
|
row_sizes = NULL;
|
|
col_sizes = NULL;
|
|
top = NULL;
|
|
zero = NULL;
|
|
odd = NULL;
|
|
blocks = NULL;
|
|
dist = NULL;
|
|
}
|
|
|
|
HDDMatrix::~HDDMatrix()
|
|
{
|
|
int i, j;
|
|
|
|
// free all row nodes
|
|
if (row_sizes && row_tables) {
|
|
for (i = 0; i < num_levels; i++) {
|
|
if (!row_tables[i]) continue;
|
|
for (j = 0; j < row_sizes[i]; j++) {
|
|
// free sparse matrix if there is one
|
|
if (row_tables[i][j] && row_tables[i][j]->sm.ptr) {
|
|
if (row_major) {
|
|
if (!compact_sm) delete (RMSparseMatrix *)row_tables[i][j]->sm.ptr;
|
|
else delete (CMSRSparseMatrix *)row_tables[i][j]->sm.ptr;
|
|
} else {
|
|
if (!compact_sm) delete (CMSparseMatrix *)row_tables[i][j]->sm.ptr;
|
|
else delete (CMSCSparseMatrix *)row_tables[i][j]->sm.ptr;
|
|
}
|
|
}
|
|
// free node
|
|
if (row_tables[i][j]) delete row_tables[i][j];
|
|
}
|
|
// free node table
|
|
if (row_tables[i]) delete[] row_tables[i];
|
|
}
|
|
// free all terminal nodes
|
|
i = num_levels;
|
|
for (j = 0; j < row_sizes[i]; j++) {
|
|
if (row_tables[i][j]) delete row_tables[i][j];
|
|
}
|
|
if (row_tables[i]) delete[] row_tables[i];
|
|
}
|
|
// free all column nodes
|
|
if (col_sizes && col_tables) {
|
|
for (i = 0; i < num_levels; i++) {
|
|
if (!col_tables[i]) continue;
|
|
for (j = 0; j < col_sizes[i]; j++) {
|
|
if (col_tables[i][j]) delete col_tables[i][j];
|
|
}
|
|
if (col_tables[i]) delete[] col_tables[i];
|
|
}
|
|
}
|
|
// free other tables
|
|
if (row_lists) delete[] row_lists;
|
|
if (col_lists) delete[] col_lists;
|
|
if (row_tables) delete[] row_tables;
|
|
if (col_tables) delete[] col_tables;
|
|
if (row_sizes) delete[] row_sizes;
|
|
if (col_sizes) delete[] col_sizes;
|
|
// free block storage
|
|
if (blocks) delete blocks;
|
|
// free distinct matrix values
|
|
if (dist) delete[] dist;
|
|
}
|
|
|
|
HDDBlocks::HDDBlocks()
|
|
{
|
|
// make sure pointers are init'ed to null
|
|
blocks = NULL;
|
|
rowscols = NULL;
|
|
counts = NULL;
|
|
offsets = NULL;
|
|
}
|
|
|
|
HDDBlocks::~HDDBlocks()
|
|
{
|
|
if (blocks) delete blocks;
|
|
if (rowscols) delete rowscols;
|
|
if (counts) { if (use_counts) delete counts; else delete (int*)counts; }
|
|
if (offsets) delete offsets;
|
|
}
|
|
|
|
HDDMatrices::HDDMatrices()
|
|
{
|
|
nm = 0;
|
|
// make sure pointers are init'ed to null
|
|
choices = NULL;
|
|
cubes = NULL;
|
|
}
|
|
|
|
HDDMatrices::~HDDMatrices()
|
|
{
|
|
int i;
|
|
|
|
// go thru all choices
|
|
for (i = 0; i < nm; i++) {
|
|
if (choices[i]) delete choices[i];
|
|
if (cubes[i]) Cudd_RecursiveDeref(ddman, cubes[i]);
|
|
}
|
|
// free arrays
|
|
if (choices) delete[] choices;
|
|
if (cubes) delete[] cubes;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
// Methods for constructing offset-labelled MTBBDs
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
// builds an offset-labelled mtbbd for a matrix (from an mtbdd)
|
|
// throws std::bad_alloc on out-of-memory
|
|
|
|
HDDMatrix *build_hdd_matrix(DdNode *matrix, DdNode **rvars, DdNode **cvars, int num_vars, ODDNode *odd, bool row_major)
|
|
{ return build_hdd_matrix(matrix, rvars, cvars, num_vars, odd, row_major, false); }
|
|
|
|
HDDMatrix *build_hdd_matrix(DdNode *matrix, DdNode **rvars, DdNode **cvars, int num_vars, ODDNode *odd, bool row_major, bool transpose)
|
|
{
|
|
int i, j;
|
|
HDDMatrix *res = NULL;
|
|
HDDNode *ptr = NULL;
|
|
|
|
// try/catch for memory allocation/deallocation
|
|
try {
|
|
|
|
// create data structure and store global pointer to it
|
|
res = new HDDMatrix();
|
|
hddm = res;
|
|
|
|
// build lists to store hdd nodes
|
|
res->row_lists = new HDDNode*[num_vars+1];
|
|
for (i = 0; i < num_vars+1; i++) res->row_lists[i] = NULL;
|
|
res->col_lists = new HDDNode*[num_vars];
|
|
for (i = 0; i < num_vars; i++) res->col_lists[i] = NULL;
|
|
res->row_tables = new HDDNode**[num_vars+1];
|
|
for (i = 0; i < num_vars+1; i++) res->row_tables[i] = NULL;
|
|
res->col_tables = new HDDNode**[num_vars];
|
|
for (i = 0; i < num_vars; i++) res->col_tables[i] = NULL;
|
|
res->row_sizes = new int[num_vars+1];
|
|
for (i = 0; i < num_vars+1; i++) res->row_sizes[i] = 0;
|
|
res->col_sizes = new int[num_vars];
|
|
for (i = 0; i < num_vars; i++) res->col_sizes[i] = 0;
|
|
// reset node counter
|
|
res->num_nodes = 0;
|
|
|
|
// create zero constant (special case)
|
|
res->num_nodes++;
|
|
res->zero = new HDDNode();
|
|
res->zero->type.kids.e = NULL;
|
|
res->zero->type.kids.t = NULL;
|
|
res->zero->off.val = 0;
|
|
res->zero->off2.val = 0;
|
|
res->zero->sm.ptr = NULL;
|
|
res->zero->next = NULL;
|
|
// and store global copy of pointer
|
|
zero = res->zero;
|
|
|
|
// call recursive bit
|
|
res->top = build_hdd_matrix_rowrec(matrix, rvars, cvars, num_vars, 0, odd, odd);
|
|
|
|
// convert node storage from linked lists to arrays
|
|
for (i = 0; i < num_vars+1; i++) {
|
|
res->row_tables[i] = new HDDNode*[res->row_sizes[i]];
|
|
j = 0;
|
|
ptr = res->row_lists[i];
|
|
while (ptr != NULL) {
|
|
res->row_tables[i][j++] = ptr;
|
|
ptr = ptr->next;
|
|
}
|
|
}
|
|
for (i = 0; i < num_vars; i++) {
|
|
res->col_tables[i] = new HDDNode*[res->col_sizes[i]];
|
|
j = 0;
|
|
ptr = res->col_lists[i];
|
|
while (ptr != NULL) {
|
|
res->col_tables[i][j++] = ptr;
|
|
ptr = ptr->next;
|
|
}
|
|
}
|
|
|
|
// go thru all nodes and
|
|
// (1) store actual offset (int) not odd ptr
|
|
// (2) set sparse matrix pointer to null
|
|
for (i = 0; i < num_vars+1; i++) {
|
|
for (j = 0; j < res->row_sizes[i]; j++) {
|
|
res->row_tables[i][j]->off.val = res->row_tables[i][j]->off.ptr->eoff;
|
|
res->row_tables[i][j]->sm.ptr = NULL;
|
|
}
|
|
}
|
|
for (i = 0; i < num_vars; i++) {
|
|
for (j = 0; j < res->col_sizes[i]; j++) {
|
|
res->col_tables[i][j]->off.val = res->col_tables[i][j]->off.ptr->eoff;
|
|
res->col_tables[i][j]->sm.ptr = NULL;
|
|
}
|
|
}
|
|
|
|
// fill up other fields with info/defaults
|
|
res->row_major = row_major;
|
|
res->compact_b = true;
|
|
res->compact_sm = true;
|
|
res->num_levels = num_vars;
|
|
res->l_b = 0;
|
|
res->l_sm = 0;
|
|
res->num_b = 0;
|
|
res->num_sm = 0;
|
|
res->mem_nodes = (res->num_nodes * sizeof(HDDNode)) / 1024.0;
|
|
res->mem_b = 0;
|
|
res->mem_sm = 0;
|
|
res->odd = odd;
|
|
res->blocks = NULL;
|
|
res->dist = NULL;
|
|
res->dist_num = 0;
|
|
res->dist_shift = 0;
|
|
res->dist_mask = 0;
|
|
|
|
// try/catch for memory allocation/deallocation
|
|
} catch(std::bad_alloc e) {
|
|
if (res) delete res;
|
|
throw e;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
// recursive part of build_hdd_matrix
|
|
|
|
HDDNode *build_hdd_matrix_rowrec(DdNode *dd, DdNode **rvars, DdNode **cvars, int num_vars, int level, ODDNode *row, ODDNode *col)
|
|
{
|
|
HDDNode *ptr, *hdd_e, *hdd_t;
|
|
DdNode *e, *t;
|
|
|
|
// check for zero terminal
|
|
if (dd == Cudd_ReadZero(ddman)) {
|
|
return zero;
|
|
}
|
|
|
|
// see if we already have the required node stored
|
|
ptr = hddm->row_lists[level];
|
|
while (ptr != NULL) {
|
|
if (((DdNode*)(ptr->sm.ptr) == dd) && (ptr->off.ptr == row) && (ptr->off2.ptr == col)) break;
|
|
// use this instead to check effect on node increase
|
|
// if (((DdNode*)(ptr->sm.ptr) == dd)) break;
|
|
ptr = ptr->next;
|
|
}
|
|
// if so, return it
|
|
if (ptr != NULL) {
|
|
return ptr;
|
|
}
|
|
|
|
// otherwise go on and create it...
|
|
|
|
// if it's a terminal node, it's easy...
|
|
if (level == num_vars) {
|
|
hddm->num_nodes++;
|
|
ptr = new HDDNode();
|
|
ptr->type.val = Cudd_V(dd);
|
|
ptr->off.ptr = row;
|
|
ptr->off2.ptr = col;
|
|
ptr->sm.ptr = (void *)dd;
|
|
ptr->next = hddm->row_lists[num_vars];
|
|
hddm->row_lists[num_vars] = ptr;
|
|
hddm->row_sizes[num_vars]++;
|
|
return ptr;
|
|
}
|
|
|
|
// if not, have to recurse before creation
|
|
if (dd->index > rvars[level]->index) {
|
|
e = t = dd;
|
|
}
|
|
else {
|
|
e = Cudd_E(dd);
|
|
t = Cudd_T(dd);
|
|
}
|
|
hdd_e = build_hdd_matrix_colrec(e, rvars, cvars, num_vars, level, row->e, col);
|
|
hdd_t = build_hdd_matrix_colrec(t, rvars, cvars, num_vars, level, row->t, col);
|
|
hddm->num_nodes++;
|
|
ptr = new HDDNode();
|
|
ptr->type.kids.e = hdd_e;
|
|
ptr->type.kids.t = hdd_t;
|
|
ptr->off.ptr = row;
|
|
ptr->off2.ptr = col;
|
|
ptr->sm.ptr = (void *)dd;
|
|
ptr->next = hddm->row_lists[level];
|
|
hddm->row_lists[level] = ptr;
|
|
hddm->row_sizes[level]++;
|
|
return ptr;
|
|
}
|
|
|
|
HDDNode *build_hdd_matrix_colrec(DdNode *dd, DdNode **rvars, DdNode **cvars, int num_vars, int level, ODDNode *row, ODDNode *col)
|
|
{
|
|
HDDNode *ptr, *hdd_e, *hdd_t;
|
|
DdNode *e, *t;
|
|
|
|
// check for zero terminal
|
|
if (dd == Cudd_ReadZero(ddman)) {
|
|
return zero;
|
|
}
|
|
|
|
// see if we already have the required node stored
|
|
ptr = hddm->col_lists[level];
|
|
while (ptr != NULL) {
|
|
if (((DdNode*)(ptr->sm.ptr) == dd) && (ptr->off.ptr == col) && (ptr->off2.ptr == row)) break;
|
|
// use this instead to check effect on node increase
|
|
// if (((DdNode*)(ptr->sm.ptr) == dd)) break;
|
|
ptr = ptr->next;
|
|
}
|
|
// if so, return it
|
|
if (ptr != NULL) {
|
|
return ptr;
|
|
}
|
|
|
|
// otherwise go on and create it...
|
|
|
|
// can't be a terminal node so recurse before creation
|
|
if (dd->index > cvars[level]->index) {
|
|
e = t = dd;
|
|
}
|
|
else {
|
|
e = Cudd_E(dd);
|
|
t = Cudd_T(dd);
|
|
}
|
|
hdd_e = build_hdd_matrix_rowrec(e, rvars, cvars, num_vars, level+1, row, col->e);
|
|
hdd_t = build_hdd_matrix_rowrec(t, rvars, cvars, num_vars, level+1, row, col->t);
|
|
hddm->num_nodes++;
|
|
ptr = new HDDNode();
|
|
ptr->type.kids.e = hdd_e;
|
|
ptr->type.kids.t = hdd_t;
|
|
ptr->off.ptr = col;
|
|
ptr->off2.ptr = row;
|
|
ptr->sm.ptr = (void *)dd;
|
|
ptr->next = hddm->col_lists[level];
|
|
hddm->col_lists[level] = ptr;
|
|
hddm->col_sizes[level]++;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
// split offset-labelled mtbdd into blocks
|
|
|
|
void split_hdd_matrix(HDDMatrix *hm, bool compact_b, bool meet)
|
|
{ return split_hdd_matrix(hm, compact_b, meet, false); }
|
|
|
|
void split_hdd_matrix(HDDMatrix *hm, bool compact_b, bool meet, bool transpose)
|
|
{
|
|
int i, n, max;
|
|
HDDBlocks *blocks = NULL;
|
|
|
|
// try/catch for memory allocation/deallocation
|
|
try {
|
|
|
|
// store some info globally
|
|
hddm = hm;
|
|
zero = hddm->zero;
|
|
|
|
// initialise variables
|
|
hddm->compact_b = compact_b;
|
|
hddm->num_b = 0;
|
|
hddm->mem_b = 0;
|
|
|
|
// now we choose a value for l_b
|
|
// first priority is given to the 'meet' flag: if true, l_b and l_sm must meet
|
|
// failing that, if the user specified a value, we use that (reducing if necessary)
|
|
// lastly, we compute the max level we can afford without exceeding mem limits
|
|
if (meet) {
|
|
hddm->l_b = hddm->num_levels - hddm->l_sm;
|
|
}
|
|
else if (num_sor_levels != -1) {
|
|
hddm->l_b = num_sor_levels;
|
|
if (hddm->l_b + hddm->l_sm > hddm->num_levels) hddm->l_b = hddm->num_levels - hddm->l_sm;
|
|
}
|
|
else {
|
|
// no memory-based heuristic yet - just use 2/5
|
|
hddm->l_b = hddm->num_levels*2/5;
|
|
if (hddm->l_b + hddm->l_sm > hddm->num_levels) hddm->l_b = hddm->num_levels - hddm->l_sm;
|
|
}
|
|
|
|
// allocate storage
|
|
blocks = new HDDBlocks();
|
|
hddm->blocks = blocks;
|
|
|
|
// if necessary, store number of distinct pointers and related info
|
|
if (hddm->compact_b) {
|
|
blocks->dist_num = hddm->row_sizes[hddm->l_b];
|
|
blocks->dist_shift = (int)ceil(logtwo(blocks->dist_num));
|
|
if (blocks->dist_shift == 0) blocks->dist_shift++;
|
|
blocks->dist_mask = (1 << blocks->dist_shift) - 1;
|
|
}
|
|
|
|
// compute n
|
|
blocks->n = 0;
|
|
traverse_odd_rec(hddm->odd, 0, hddm->num_levels, 0, 1);
|
|
n = blocks->n;
|
|
|
|
// see if compact storage is feasible; if not, abandon it
|
|
if (hddm->compact_b) {
|
|
if (blocks->dist_shift + (int)ceil(logtwo(n)) > 8*sizeof(unsigned int)) {
|
|
hddm->compact_b = false;
|
|
}
|
|
}
|
|
|
|
// compute block offsets
|
|
blocks->offsets = new int[n+1];
|
|
hddm->mem_b += (((n+1) * sizeof(int)) / 1024.0);
|
|
for (i = 0; i < n+1; i++) blocks->offsets[i] = 0;
|
|
// last offset will always be num states
|
|
blocks->offsets[n] = hddm->odd->eoff+hddm->odd->toff;
|
|
// compute offsets recursively
|
|
blocks->n = 0;
|
|
traverse_odd_rec(hddm->odd, 0, hddm->num_levels, 0, 2);
|
|
|
|
// compute max block size (gap b/e offsets)
|
|
blocks->max = blocks->offsets[1] - blocks->offsets[0];
|
|
for (i = 1; i < blocks->n; i++) {
|
|
if( blocks->offsets[i+1] - blocks->offsets[i] > blocks->max) {
|
|
blocks->max = blocks->offsets[i+1] - blocks->offsets[i] ;
|
|
}
|
|
}
|
|
|
|
// allocate temporary array to store start of each row/col
|
|
starts = NULL; starts = new int[n+1];
|
|
// see how many nonzeros are in each row/column (depending on row_major flag)
|
|
for (i = 0; i < n+1; i++) starts[i] = 0;
|
|
blocks->nnz = 0;
|
|
traverse_hdd_rec(hddm->top, 0, hddm->l_b, 0, 0, hddm->row_major?1:2, transpose);
|
|
// and use this to compute the starts information
|
|
// (and at same time, compute max num entries in a row/col)
|
|
max = 0;
|
|
for (i = 1; i < n+1; i++) {
|
|
if (starts[i] > max) max = starts[i];
|
|
starts[i] += starts[i-1];
|
|
}
|
|
// max num entries determines whether we store counts or starts:
|
|
blocks->use_counts = (max < (unsigned int)(1 << (8*sizeof(unsigned char))));
|
|
|
|
// traversal above also computed nnz so now allocate arrays
|
|
blocks->rowscols = new unsigned int[blocks->nnz];
|
|
hddm->mem_b += ((blocks->nnz * sizeof(unsigned int)) / 1024.0);
|
|
for (i = 0; i < blocks->nnz; i++) blocks->rowscols[i] = 0;
|
|
if (!hddm->compact_b) {
|
|
blocks->blocks = new HDDNode*[blocks->nnz];
|
|
hddm->mem_b += ((blocks->nnz * sizeof(HDDNode *)) / 1024.0);
|
|
for (i = 0; i < blocks->nnz; i++) blocks->blocks[i] = NULL;
|
|
}
|
|
|
|
// then fill it up
|
|
if (!hddm->compact_b) {
|
|
traverse_hdd_rec(hddm->top, 0, hddm->l_b, 0, 0, hddm->row_major?3:4, transpose);
|
|
} else {
|
|
traverse_hdd_rec(hddm->top, 0, hddm->l_b, 0, 0, hddm->row_major?5:6, transpose);
|
|
}
|
|
|
|
// recompute starts info because we've messed with it during previous traversal
|
|
for (i = n; i > 0; i--) {
|
|
starts[i] = starts[i-1];
|
|
}
|
|
starts[0] = 0;
|
|
// assuming it's safe to do so, replace starts with (smaller) array of counts
|
|
// if not, we keep the starts array and use that
|
|
if (blocks->use_counts) {
|
|
blocks->counts = new unsigned char[blocks->n];
|
|
hddm->mem_b += ((n * sizeof(unsigned char)) / 1024.0);
|
|
for (i = 0; i < n; i++) {
|
|
blocks->counts[i] = (unsigned char)(starts[i+1] - starts[i]);
|
|
}
|
|
delete[] starts; starts = NULL;
|
|
}
|
|
else {
|
|
blocks->counts = (unsigned char*)starts;
|
|
hddm->mem_b += ((n * sizeof(int)) / 1024.0);
|
|
}
|
|
|
|
// try/catch for memory allocation/deallocation
|
|
} catch(std::bad_alloc e) {
|
|
if (blocks) delete blocks;
|
|
hddm->blocks = NULL;
|
|
if (starts) delete[] starts;
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
// add explicit sparse matrices on to the hdd
|
|
|
|
void add_sparse_matrices(HDDMatrix *hm, bool compact_sm, bool diags_meet)
|
|
{ return add_sparse_matrices(hm, compact_sm, diags_meet, false); }
|
|
|
|
void add_sparse_matrices(HDDMatrix *hm, bool compact_sm, bool diags_meet, bool transpose)
|
|
{
|
|
int i, j, k, n, nnz, l, h, i_sm, rowcol;
|
|
double mem_est;
|
|
bool mem_out;
|
|
HDDNode *node = NULL;
|
|
|
|
// try/catch for memory allocation/deallocation
|
|
try {
|
|
|
|
// store some info globally
|
|
hddm = hm;
|
|
zero = hddm->zero;
|
|
|
|
// initialise variables
|
|
hddm->compact_sm = compact_sm;
|
|
hddm->num_sm = 0;
|
|
hddm->mem_sm = 0;
|
|
|
|
// first, we initialise all sm/off2
|
|
// (they may have been used to store other things previously)
|
|
// (sm is set to -1 because we need to know when we visit for the first time, off2 is set to 0)
|
|
for (i = 0; i < hddm->num_levels+1; i++) {
|
|
for (j = 0; j < hddm->row_sizes[i]; j++) {
|
|
hddm->row_tables[i][j]->sm.val = -1;
|
|
hddm->row_tables[i][j]->off2.val = 0;
|
|
}
|
|
}
|
|
for (i = 0; i < hddm->num_levels; i++) {
|
|
for (j = 0; j < hddm->col_sizes[i]; j++) {
|
|
hddm->col_tables[i][j]->sm.val = -1;
|
|
hddm->col_tables[i][j]->off2.val = 0;
|
|
}
|
|
}
|
|
|
|
// store size (num states and nnz) of each node's matrix
|
|
// (putting them in the sm and off2 fields, respectively)
|
|
compute_n_and_nnz_rec(hddm->top, 0, hddm->num_levels, hddm->odd, hddm->odd, transpose);
|
|
|
|
// now we choose a value for l_sm
|
|
// if the user specified a value, we use that (reducing if necessary)
|
|
// if not, we compute the max level we can afford without exceeding mem limits
|
|
if (num_sb_levels != -1) {
|
|
hddm->l_sm = num_sb_levels;
|
|
if (hddm->l_sm + hddm->l_b > hddm->num_levels) hddm->l_sm = hddm->num_levels - hddm->l_b;
|
|
}
|
|
else {
|
|
// estimate memory required for each level
|
|
// and select the last level for which we don't exceed the limit
|
|
mem_out = false;
|
|
for (i = 1; i <= hddm->num_levels - hddm->l_b; i++) {
|
|
j = hddm->num_levels - i;
|
|
mem_est = 0;
|
|
for (k = 0; k < hddm->row_sizes[j]; k++) {
|
|
n = hddm->row_tables[j][k]->sm.val;
|
|
nnz = hddm->row_tables[j][k]->off2.val;
|
|
if (!compact_sm) mem_est += ((nnz*(sizeof(double)+sizeof(unsigned int))+n*sizeof(unsigned char)) / 1024.0);
|
|
else mem_est += ((nnz*(sizeof(unsigned int))+n*sizeof(unsigned char)) / 1024.0);
|
|
if (mem_est > sb_max_mem) {
|
|
mem_out = true;
|
|
break;
|
|
}
|
|
}
|
|
if (mem_out) break;
|
|
}
|
|
hddm->l_sm = mem_out ? i-1 : hddm->num_levels - hddm->l_b;
|
|
}
|
|
|
|
// compute index of level corresponding to l_sm
|
|
i_sm = hddm->num_levels - hddm->l_sm;
|
|
|
|
// if diagonal blocks will be created anyway, don't bother
|
|
if (diags_meet) if (hddm->l_sm + hddm->l_b >= hddm->num_levels) {
|
|
diags_meet = false;
|
|
}
|
|
|
|
// only actually add sparse matrices if there are any to add
|
|
if (diags_meet || hddm->l_sm > 0) {
|
|
|
|
// see if compact storage is feasible; if not, abandon it
|
|
if (hddm->compact_sm) {
|
|
// this is how many bits are free to store row/column indices
|
|
int sparebits = 8*sizeof(unsigned int) - (int)ceil(logtwo(hddm->row_sizes[hddm->num_levels]));
|
|
if (sparebits == 8*sizeof(unsigned int)) sparebits--;
|
|
// so this is the max size of sparse matrix we can afford
|
|
unsigned int maxsize = 1 << sparebits;
|
|
// go thru all sparse matrices we are about to create and make sure they're not too big
|
|
// in fact, for the case where diag_meet=true, this would be complicated and messy
|
|
// hence, in this case, we resort to an over-approximation and look at all matrices on that level (l_b)
|
|
j = diags_meet ? hddm->l_b : i_sm;
|
|
for (i = 0; i < hddm->row_sizes[j]; i++) {
|
|
// size is curently stored in sm pointer
|
|
if (hddm->row_tables[j][i]->sm.val > maxsize) {
|
|
hddm->compact_sm = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we're creating a "compact modified" sparse matrix,
|
|
// store info about the distinct values in the matrix
|
|
if (hddm->compact_sm) {
|
|
hddm->dist_num = hddm->row_sizes[hddm->num_levels];
|
|
hddm->dist_shift = (int)ceil(logtwo(hddm->dist_num));
|
|
if (hddm->dist_shift == 0) hddm->dist_shift++;
|
|
hddm->dist_mask = (1 << hddm->dist_shift) - 1;
|
|
// store all the distinct values in the matrix
|
|
hddm->dist = new double[hddm->dist_num];
|
|
hddm->mem_sm += ((hddm->dist_num * sizeof(double)) / 1024.0);
|
|
for (j = 0; j < hddm->row_sizes[hddm->num_levels]; j++) {
|
|
hddm->dist[j] = hddm->row_tables[hddm->num_levels][j]->type.val;
|
|
}
|
|
}
|
|
|
|
// now actually add the sparse matrices
|
|
|
|
// first the diagonal blocks (if necessary)
|
|
if (diags_meet) {
|
|
|
|
// stuff for block storage
|
|
int b_n = hddm->blocks->n;
|
|
HDDNode **b_blocks = hddm->blocks->blocks;
|
|
unsigned int *b_rowscols = hddm->blocks->rowscols;
|
|
unsigned char *b_counts = hddm->blocks->counts;
|
|
int *b_starts = (int *)hddm->blocks->counts;
|
|
bool b_use_counts = hddm->blocks->use_counts;
|
|
HDDNode **b_nodes = hddm->row_tables[hddm->l_b];
|
|
int b_dist_shift = hddm->blocks->dist_shift;
|
|
int b_dist_mask = hddm->blocks->dist_mask;
|
|
|
|
// go through each row/column of blocks
|
|
h = 0;
|
|
for(i = 0; i < b_n; i++) {
|
|
if (!b_use_counts) { l = b_starts[i]; h = b_starts[i+1]; }
|
|
else { l = h; h += b_counts[i]; }
|
|
// go along this row/column
|
|
for(j = l; j < h; j++) {
|
|
// get column/row index of block
|
|
rowcol = (!hddm->compact_b) ? b_rowscols[j] : ((unsigned int)(b_rowscols[j] >> b_dist_shift));
|
|
// if it's the diagonal...
|
|
if (rowcol == i) {
|
|
// if there is no sparse matrix there already, add a sparse matrix
|
|
// (we mark nodes we have done already by setting off2 to -1)
|
|
node = (!hddm->compact_b) ? b_blocks[j] : (b_nodes[(int)(b_rowscols[j] & b_dist_mask)]);
|
|
if (node->off2.val != -1) {
|
|
if (!hddm->compact_sm) {
|
|
if (hddm->row_major) {
|
|
node->sm.ptr = (void *)build_rm_sparse_matrix(node, hddm->l_b, transpose);
|
|
} else {
|
|
node->sm.ptr = (void *)build_cm_sparse_matrix(node, hddm->l_b, transpose);
|
|
}
|
|
} else {
|
|
if (hddm->row_major) {
|
|
node->sm.ptr = (void *)build_cmsr_sparse_matrix(node, hddm->l_b, transpose);
|
|
} else {
|
|
node->sm.ptr = (void *)build_cmsc_sparse_matrix(node, hddm->l_b, transpose);
|
|
}
|
|
}
|
|
// set off2 to -1 to indicate we have added a sparse matrix here
|
|
node->off2.val = -1;
|
|
// increment matrix count
|
|
hddm->num_sm++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// then all blocks at level i_sm
|
|
if (hddm->l_sm > 0) {
|
|
for (i = 0; i < hddm->row_sizes[i_sm]; i++) {
|
|
node = hddm->row_tables[i_sm][i];
|
|
if (!hddm->compact_sm) {
|
|
if (hddm->row_major) {
|
|
node->sm.ptr = (void *)build_rm_sparse_matrix(node, i_sm, transpose);
|
|
} else {
|
|
node->sm.ptr = (void *)build_cm_sparse_matrix(node, i_sm, transpose);
|
|
}
|
|
} else {
|
|
if (hddm->row_major) {
|
|
node->sm.ptr = (void *)build_cmsr_sparse_matrix(node, i_sm, transpose);
|
|
} else {
|
|
node->sm.ptr = (void *)build_cmsc_sparse_matrix(node, i_sm, transpose);
|
|
}
|
|
}
|
|
// set off2 to -1 to indicate we have added a sparse matrix here
|
|
node->off2.val = -1;
|
|
// increment matrix count
|
|
hddm->num_sm++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// finally, set all sparse matrix pointers back to null
|
|
// (except on the nodes where we have just put sparse matrices)
|
|
for (i = 0; i < hddm->num_levels+1; i++) {
|
|
for (j = 0; j < hddm->row_sizes[i]; j++) {
|
|
if (hddm->row_tables[i][j]->off2.val != -1) hddm->row_tables[i][j]->sm.ptr = NULL;
|
|
}
|
|
}
|
|
for (i = 0; i < hddm->num_levels; i++) {
|
|
for (j = 0; j < hddm->col_sizes[i]; j++) {
|
|
hddm->col_tables[i][j]->sm.ptr = NULL;
|
|
}
|
|
}
|
|
|
|
// try/catch for memory allocation/deallocation
|
|
} catch(std::bad_alloc e) {
|
|
// if we run out of memory during this function, it is going to be
|
|
// very difficult to deallocate memory cleanly (mainly because we
|
|
// have abused various pointer variables by temporarily storing
|
|
// integers in them). So, at the expense of a small memory leak...
|
|
hddm->row_tables = NULL;
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
// Utility methods for methods which construct offset-labelled MTBDDS
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
// generic function for recursive traversal of offset-labelled MTBDD
|
|
|
|
void traverse_hdd_rec(HDDNode *hdd, int level, int stop, int r, int c, int code, bool transpose)
|
|
{
|
|
HDDNode *e, *t;
|
|
int i, l, h, r2, c2, dist_num, dist_shift;
|
|
double *dist;
|
|
|
|
// if it's zero, return
|
|
if (hdd == zero) {
|
|
return;
|
|
}
|
|
|
|
// or if we've gone down far enough
|
|
else if (level == stop) {
|
|
|
|
// for code values of 1-6, we need to determine the row and/or column index
|
|
// of the current block. we do this here to avoid repetition/cluttering in
|
|
// the code below. since we only know the row/column offset for the block,
|
|
// each index is determined via a binary seacrh over the offsets array.
|
|
// the results are stored in r2 and c2, respectively.
|
|
if (code >=1 && code <=6) {
|
|
// determine row index
|
|
l=0; h=hddm->blocks->n;
|
|
while (l<h) {
|
|
r2 = (h-l)/2+l;
|
|
if (hddm->blocks->offsets[r2] == r) break;
|
|
else if (hddm->blocks->offsets[r2] < r) l = r2; else h = r2;
|
|
}
|
|
// determine column index
|
|
l=0; h=hddm->blocks->n;
|
|
while (l<h) {
|
|
c2 = (h-l)/2+l;
|
|
if (hddm->blocks->offsets[c2] == c) break;
|
|
else if (hddm->blocks->offsets[c2] < c) l = c2; else h = c2;
|
|
}
|
|
}
|
|
|
|
// do different things on the code passed in
|
|
switch (code) {
|
|
|
|
// count blocks in each row of blocks (rm/cmsr)
|
|
case 1:
|
|
starts[(transpose?c2:r2)+1]++;
|
|
hddm->blocks->nnz++;
|
|
break;
|
|
|
|
// count blocks in each column of blocks (cm/cmsc)
|
|
case 2:
|
|
starts[(transpose?r2:c2)+1]++;
|
|
hddm->blocks->nnz++;
|
|
break;
|
|
|
|
// store blocks (rm)
|
|
case 3:
|
|
hddm->blocks->blocks[starts[(transpose?c2:r2)]] = hdd;
|
|
hddm->blocks->rowscols[starts[(transpose?c2:r2)]] = (transpose?r2:c2);
|
|
starts[(transpose?c2:r2)]++;
|
|
break;
|
|
|
|
// store blocks (cm)
|
|
case 4:
|
|
hddm->blocks->blocks[starts[(transpose?r2:c2)]] = hdd;
|
|
hddm->blocks->rowscols[starts[(transpose?r2:c2)]] = (transpose?c2:r2);
|
|
starts[(transpose?r2:c2)]++;
|
|
break;
|
|
|
|
// store blocks (cmsr)
|
|
case 5:
|
|
// find block
|
|
for (i = 0; i < hddm->row_sizes[level]; i++) {
|
|
if (hddm->row_tables[level][i] == hdd) break;
|
|
}
|
|
// store block
|
|
hddm->blocks->rowscols[starts[(transpose?c2:r2)]] = (unsigned int)(((unsigned int)(transpose?r2:c2) << (unsigned int)hddm->blocks->dist_shift) + (unsigned int)i);
|
|
starts[(transpose?c2:r2)]++;
|
|
break;
|
|
|
|
// store blocks (cmsc)
|
|
case 6:
|
|
// find block
|
|
for (i = 0; i < hddm->row_sizes[level]; i++) {
|
|
if (hddm->row_tables[level][i] == hdd) break;
|
|
}
|
|
// store block
|
|
hddm->blocks->rowscols[starts[(transpose?r2:c2)]] = (unsigned int)(((unsigned int)(transpose?c2:r2) << (unsigned int)hddm->blocks->dist_shift) + (unsigned int)i);
|
|
starts[(transpose?r2:c2)]++;
|
|
break;
|
|
|
|
// count entries in each row (rm)
|
|
case 7:
|
|
starts[(transpose?c:r)+1]++;
|
|
break;
|
|
|
|
// count entries in each column (cm)
|
|
case 8:
|
|
starts[(transpose?r:c)+1]++;
|
|
break;
|
|
|
|
// store entries (rm)
|
|
case 9:
|
|
rmsm->non_zeros[starts[(transpose?c:r)]] = hdd->type.val;
|
|
rmsm->cols[starts[(transpose?c:r)]] = (transpose?r:c);
|
|
starts[(transpose?c:r)]++;
|
|
break;
|
|
|
|
// store entries (cm)
|
|
case 10:
|
|
cmsm->non_zeros[starts[(transpose?r:c)]] = hdd->type.val;
|
|
cmsm->rows[starts[(transpose?r:c)]] = (transpose?c:r);
|
|
starts[(transpose?r:c)]++;
|
|
break;
|
|
|
|
// store entries (cmsr)
|
|
case 11:
|
|
dist = hddm->dist;
|
|
dist_num = hddm->dist_num;
|
|
dist_shift = hddm->dist_shift;
|
|
for (i = 0; i < dist_num; i++) if (dist[i] == hdd->type.val) break;
|
|
cmsrsm->cols[starts[(transpose?c:r)]] = (unsigned int)(((unsigned int)(transpose?r:c) << dist_shift) + (unsigned int)i);
|
|
starts[(transpose?c:r)]++;
|
|
break;
|
|
|
|
// store entries (cmsc)
|
|
case 12:
|
|
dist = hddm->dist;
|
|
dist_num = hddm->dist_num;
|
|
dist_shift = hddm->dist_shift;
|
|
for (i = 0; i < dist_num; i++) if (dist[i] == hdd->type.val) break;
|
|
cmscsm->rows[starts[(transpose?r:c)]] = (unsigned int)(((unsigned int)(transpose?c:r) << dist_shift) + (unsigned int)i);
|
|
starts[(transpose?r:c)]++;
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// recurse - split four ways
|
|
e = hdd->type.kids.e;
|
|
if (e != zero) {
|
|
traverse_hdd_rec(e->type.kids.e, level+1, stop, r, c, code, transpose);
|
|
traverse_hdd_rec(e->type.kids.t, level+1, stop, r, c+e->off.val, code, transpose);
|
|
}
|
|
t = hdd->type.kids.t;
|
|
if (t != zero) {
|
|
traverse_hdd_rec(t->type.kids.e, level+1, stop, r+hdd->off.val, c, code, transpose);
|
|
traverse_hdd_rec(t->type.kids.t, level+1, stop, r+hdd->off.val, c+t->off.val, code, transpose);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
// generic function for recursive traversal of offset-labelled BDD
|
|
|
|
void traverse_odd_rec(ODDNode *odd, int level, int stop, int index, int code)
|
|
{
|
|
// (if either we've gone down enough levels...)
|
|
// (or there's no child node - i.e. points to zero terminal)
|
|
if (odd->dd == Cudd_ReadZero(ddman)) {
|
|
return;
|
|
}
|
|
if (level == hddm->l_b) {
|
|
switch (code) {
|
|
|
|
// compute n
|
|
case 1:
|
|
hddm->blocks->n++; break;
|
|
|
|
// fill up offsets array
|
|
case 2:
|
|
hddm->blocks->offsets[hddm->blocks->n] = (int)index;
|
|
hddm->blocks->n++;
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// recurse
|
|
traverse_odd_rec(odd->e, level+1, stop, index, code);
|
|
traverse_odd_rec(odd->t, level+1, stop, index+odd->eoff, code);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
// compute the size (num states and nnz) of matrix corresponding to each offset-labelled MTBDD node
|
|
// (and store in sm and off2 fields, respectively)
|
|
|
|
int compute_n_and_nnz_rec(HDDNode *hdd, int level, int num_levels, ODDNode *row, ODDNode *col, bool transpose)
|
|
{
|
|
HDDNode *e, *t;
|
|
|
|
// if it's the zero node...
|
|
if (hdd == zero) {
|
|
hdd->sm.val = 0;
|
|
hdd->off2.val = 0;
|
|
return 0;
|
|
}
|
|
// if it's at the bottom...
|
|
if (level == num_levels) {
|
|
hdd->sm.val = 0;
|
|
hdd->off2.val = 1;
|
|
return 1;
|
|
}
|
|
|
|
// check if we've already done this node
|
|
if (hdd->sm.val != -1) {
|
|
return hdd->off2.val;
|
|
}
|
|
|
|
// store n (note we count rows or columns depending on transpose/row_major)
|
|
if ((hddm->row_major && !transpose) || (!hddm->row_major && transpose)) {
|
|
hdd->sm.val = row->eoff + row->toff;
|
|
} else {
|
|
hdd->sm.val = col->eoff + col->toff;
|
|
}
|
|
// recurse and store nnz
|
|
hdd->off2.val = 0;
|
|
e = hdd->type.kids.e;
|
|
if (e != zero) {
|
|
hdd->off2.val += compute_n_and_nnz_rec(e->type.kids.e, level+1, num_levels, row->e, col->e, transpose);
|
|
hdd->off2.val += compute_n_and_nnz_rec(e->type.kids.t, level+1, num_levels, row->e, col->t, transpose);
|
|
}
|
|
t = hdd->type.kids.t;
|
|
if (t != zero) {
|
|
hdd->off2.val += compute_n_and_nnz_rec(t->type.kids.e, level+1, num_levels, row->t, col->e, transpose);
|
|
hdd->off2.val += compute_n_and_nnz_rec(t->type.kids.t, level+1, num_levels, row->t, col->t, transpose);
|
|
}
|
|
return hdd->off2.val;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
// build the explicit sparse matrix for a hdd node: 4 cases, one for each type
|
|
|
|
RMSparseMatrix *build_rm_sparse_matrix(HDDNode *hdd, int level, bool transpose)
|
|
{
|
|
int i, n, nnz, max;
|
|
|
|
// try/catch for memory allocation/deallocation
|
|
try {
|
|
|
|
// create the data structure
|
|
rmsm = NULL; rmsm = new RMSparseMatrix();
|
|
rmsm->n = n = hdd->sm.val;
|
|
rmsm->nnz = nnz = hdd->off2.val;
|
|
|
|
// allocate temporary array to store start of each row
|
|
starts = NULL; starts = new int[n+1];
|
|
// see how many nonzeros are in each row
|
|
for (i = 0; i < n+1; i++) starts[i] = 0;
|
|
traverse_hdd_rec(hdd, level, hddm->num_levels, 0, 0, 7, transpose);
|
|
// and use this to compute the starts information
|
|
// (and at same time, compute max num entries in a row)
|
|
max = 0;
|
|
for (i = 1; i < n+1; i++) {
|
|
if (starts[i] > max) max = starts[i];
|
|
starts[i] += starts[i-1];
|
|
}
|
|
// max num entries determines whether we store counts or starts:
|
|
rmsm->use_counts = (max < (unsigned int)(1 << (8*sizeof(unsigned char))));
|
|
|
|
// allocate arrays
|
|
rmsm->non_zeros = new double[nnz];
|
|
hddm->mem_sm += ((nnz * sizeof(double)) / 1024.0);
|
|
rmsm->cols = new unsigned int[nnz];
|
|
hddm->mem_sm += ((nnz * sizeof(unsigned int)) / 1024.0);
|
|
|
|
// fill up arrays
|
|
traverse_hdd_rec(hdd, level, hddm->num_levels, 0, 0, 9, transpose);
|
|
|
|
// recompute starts info because we've messed with it during previous traversal
|
|
for (i = n; i > 0; i--) {
|
|
starts[i] = starts[i-1];
|
|
}
|
|
starts[0] = 0;
|
|
// assuming it's safe to do so, replace starts with (smaller) array of counts
|
|
// if not, we keep the starts array and use that
|
|
if (rmsm->use_counts) {
|
|
rmsm->row_counts = new unsigned char[n];
|
|
hddm->mem_sm += ((n * sizeof(unsigned char)) / 1024.0);
|
|
for (i = 0; i < n; i++) {
|
|
rmsm->row_counts[i] = (unsigned char)(starts[i+1] - starts[i]);
|
|
}
|
|
delete[] starts; starts = NULL;
|
|
}
|
|
else {
|
|
rmsm->row_counts = (unsigned char*)starts;
|
|
hddm->mem_sm += ((n * sizeof(int)) / 1024.0);
|
|
}
|
|
|
|
// try/catch for memory allocation/deallocation
|
|
} catch(std::bad_alloc e) {
|
|
if (rmsm) delete rmsm;
|
|
if (starts) delete[] starts;
|
|
throw e;
|
|
}
|
|
|
|
return rmsm;
|
|
}
|
|
|
|
CMSparseMatrix *build_cm_sparse_matrix(HDDNode *hdd, int level, bool transpose)
|
|
{
|
|
int i, n, nnz, max;
|
|
|
|
// try/catch for memory allocation/deallocation
|
|
try {
|
|
|
|
// create the data structure
|
|
cmsm = NULL; cmsm = new CMSparseMatrix();
|
|
cmsm->n = n = hdd->sm.val;
|
|
cmsm->nnz = nnz = hdd->off2.val;
|
|
|
|
// allocate temporary array to store start of each col
|
|
starts = NULL; starts = new int[n+1];
|
|
// see how many nonzeros are in each column
|
|
for (i = 0; i < n+1; i++) starts[i] = 0;
|
|
traverse_hdd_rec(hdd, level, hddm->num_levels, 0, 0, 8, transpose);
|
|
// and use this to compute the starts information
|
|
// (and at same time, compute max num entries in a col)
|
|
max = 0;
|
|
for (i = 1; i < n+1; i++) {
|
|
if (starts[i] > max) max = starts[i];
|
|
starts[i] += starts[i-1];
|
|
}
|
|
// max num entries determines whether we store counts or starts:
|
|
cmsm->use_counts = (max < (unsigned int)(1 << (8*sizeof(unsigned char))));
|
|
|
|
// allocate arrays
|
|
cmsm->non_zeros = new double[nnz];
|
|
hddm->mem_sm += ((nnz * sizeof(double)) / 1024.0);
|
|
cmsm->rows = new unsigned int[nnz];
|
|
hddm->mem_sm += ((nnz * sizeof(unsigned int)) / 1024.0);
|
|
|
|
// fill up arrays
|
|
traverse_hdd_rec(hdd, level, hddm->num_levels, 0, 0, 10, transpose);
|
|
|
|
// recompute starts info because we've messed with it during previous traversal
|
|
for (i = n; i > 0; i--) {
|
|
starts[i] = starts[i-1];
|
|
}
|
|
starts[0] = 0;
|
|
// assuming it's safe to do so, replace starts with (smaller) array of counts
|
|
// if not, we keep the starts array and use that
|
|
if (cmsm->use_counts) {
|
|
cmsm->col_counts = new unsigned char[n];
|
|
hddm->mem_sm += ((n * sizeof(unsigned char)) / 1024.0);
|
|
for (i = 0; i < n; i++) {
|
|
cmsm->col_counts[i] = (unsigned char)(starts[i+1] - starts[i]);
|
|
}
|
|
delete[] starts; starts = NULL;
|
|
}
|
|
else {
|
|
cmsm->col_counts = (unsigned char*)starts;
|
|
hddm->mem_sm += ((n * sizeof(int)) / 1024.0);
|
|
}
|
|
|
|
// try/catch for memory allocation/deallocation
|
|
} catch(std::bad_alloc e) {
|
|
if (cmsm) delete cmsm;
|
|
if (starts) delete[] starts;
|
|
throw e;
|
|
}
|
|
|
|
return cmsm;
|
|
}
|
|
|
|
CMSRSparseMatrix *build_cmsr_sparse_matrix(HDDNode *hdd, int level, bool transpose)
|
|
{
|
|
int i, n, nnz, max;
|
|
|
|
// try/catch for memory allocation/deallocation
|
|
try {
|
|
|
|
// create the data structure
|
|
cmsrsm = NULL; cmsrsm = new CMSRSparseMatrix();
|
|
cmsrsm->n = n = hdd->sm.val;
|
|
cmsrsm->nnz = nnz = hdd->off2.val;
|
|
// info about distinct vals will be shared across the sparse matrices
|
|
// so set this array to null to indicate that we don't use it
|
|
cmsrsm->dist = NULL;
|
|
|
|
// allocate temporary array to store start of each row
|
|
starts = NULL; starts = new int[n+1];
|
|
// see how many nonzeros are in each row
|
|
for (i = 0; i < n+1; i++) starts[i] = 0;
|
|
traverse_hdd_rec(hdd, level, hddm->num_levels, 0, 0, 7, transpose);
|
|
// and use this to compute the starts information
|
|
// (and at same time, compute max num entries in a row)
|
|
max = 0;
|
|
for (i = 1; i < n+1; i++) {
|
|
if (starts[i] > max) max = starts[i];
|
|
starts[i] += starts[i-1];
|
|
}
|
|
// max num entries determines whether we store counts or starts:
|
|
cmsrsm->use_counts = (max < (unsigned int)(1 << (8*sizeof(unsigned char))));
|
|
|
|
// allocate arrays
|
|
cmsrsm->cols = new unsigned int[nnz];
|
|
hddm->mem_sm += ((nnz * sizeof(unsigned int)) / 1024.0);
|
|
|
|
// fill up arrays
|
|
traverse_hdd_rec(hdd, level, hddm->num_levels, 0, 0, 11, transpose);
|
|
|
|
// recompute starts info because we've messed with it during previous traversal
|
|
for (i = n; i > 0; i--) {
|
|
starts[i] = starts[i-1];
|
|
}
|
|
starts[0] = 0;
|
|
// assuming it's safe to do so, replace starts with (smaller) array of counts
|
|
// if not, we keep the starts array and use that
|
|
if (cmsrsm->use_counts) {
|
|
cmsrsm->row_counts = new unsigned char[n];
|
|
hddm->mem_sm += ((n * sizeof(unsigned char)) / 1024.0);
|
|
for (i = 0; i < n; i++) {
|
|
cmsrsm->row_counts[i] = (unsigned char)(starts[i+1] - starts[i]);
|
|
}
|
|
delete[] starts; starts = NULL;
|
|
}
|
|
else {
|
|
cmsrsm->row_counts = (unsigned char*)starts;
|
|
hddm->mem_sm += ((n * sizeof(int)) / 1024.0);
|
|
}
|
|
|
|
// try/catch for memory allocation/deallocation
|
|
} catch(std::bad_alloc e) {
|
|
if (cmsrsm) delete cmsrsm;
|
|
if (starts) delete[] starts;
|
|
throw e;
|
|
}
|
|
|
|
return cmsrsm;
|
|
}
|
|
|
|
CMSCSparseMatrix *build_cmsc_sparse_matrix(HDDNode *hdd, int level, bool transpose)
|
|
{
|
|
int i, n, nnz, max;
|
|
|
|
// try/catch for memory allocation/deallocation
|
|
try {
|
|
|
|
// create the data structure
|
|
cmscsm = NULL; cmscsm = new CMSCSparseMatrix();
|
|
cmscsm->n = n = hdd->sm.val;
|
|
cmscsm->nnz = nnz = hdd->off2.val;
|
|
// info about distinct vals will be shared across the sparse matrices
|
|
// so set this array to null to indicate that we don't use it
|
|
cmscsm->dist = NULL;
|
|
|
|
// allocate temporary array to store start of each col
|
|
starts = NULL; starts = new int[n+1];
|
|
// see how many nonzeros are in each column
|
|
for (i = 0; i < n+1; i++) starts[i] = 0;
|
|
traverse_hdd_rec(hdd, level, hddm->num_levels, 0, 0, 8, transpose);
|
|
// and use this to compute the starts information
|
|
// (and at same time, compute max num entries in a col)
|
|
max = 0;
|
|
for (i = 1; i < n+1; i++) {
|
|
if (starts[i] > max) max = starts[i];
|
|
starts[i] += starts[i-1];
|
|
}
|
|
// max num entries determines whether we store counts or starts:
|
|
cmscsm->use_counts = (max < (unsigned int)(1 << (8*sizeof(unsigned char))));
|
|
|
|
// allocate arrays
|
|
cmscsm->rows = new unsigned int[nnz];
|
|
hddm->mem_sm += ((nnz * sizeof(unsigned int)) / 1024.0);
|
|
|
|
// fill up arrays
|
|
traverse_hdd_rec(hdd, level, hddm->num_levels, 0, 0, 12, transpose);
|
|
|
|
// recompute starts info because we've messed with it during previous traversal
|
|
for (i = n; i > 0; i--) {
|
|
starts[i] = starts[i-1];
|
|
}
|
|
starts[0] = 0;
|
|
// assuming it's safe to do so, replace starts with (smaller) array of counts
|
|
// if not, we keep the starts array and use that
|
|
if (cmscsm->use_counts) {
|
|
cmscsm->col_counts = new unsigned char[n];
|
|
hddm->mem_sm += ((n * sizeof(unsigned char)) / 1024.0);
|
|
for (i = 0; i < n; i++) {
|
|
cmscsm->col_counts[i] = (unsigned char)(starts[i+1] - starts[i]);
|
|
}
|
|
delete[] starts; starts = NULL;
|
|
}
|
|
else {
|
|
cmscsm->col_counts = (unsigned char*)starts;
|
|
hddm->mem_sm += ((n * sizeof(int)) / 1024.0);
|
|
}
|
|
|
|
// try/catch for memory allocation/deallocation
|
|
} catch(std::bad_alloc e) {
|
|
if (cmscsm) delete cmscsm;
|
|
if (starts) delete[] starts;
|
|
throw e;
|
|
}
|
|
|
|
return cmscsm;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
// MDP variants of methods for constructing offset-labelled MTBDDs
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
// builds hybrid mtbdd matrices from mtbdd (for mdp models)
|
|
// if existing_mdp is non-null, use this for nm, cubes, etc.
|
|
|
|
HDDMatrices *build_hdd_matrices_mdp(DdNode *mdp, HDDMatrices *existing_mdp, DdNode **rvars, DdNode **cvars, int num_vars, DdNode **ndvars, int num_ndvars, ODDNode *odd)
|
|
{
|
|
int i;
|
|
DdNode *tmp;
|
|
HDDMatrix *hddm = NULL;
|
|
HDDMatrices *res = NULL;
|
|
|
|
// try/catch for memory allocation/deallocation
|
|
try {
|
|
|
|
// create new data structure
|
|
res = new HDDMatrices();
|
|
|
|
// get nm (number of matrices to split into)
|
|
if (existing_mdp != NULL) {
|
|
// just copy existing stats if appropriate
|
|
res->nm = existing_mdp->nm;
|
|
} else {
|
|
// otherwise compute nm from scratch
|
|
Cudd_Ref(mdp);
|
|
tmp = DD_GreaterThan(ddman, mdp, 0);
|
|
tmp = DD_ThereExists(ddman, tmp, rvars, num_vars);
|
|
tmp = DD_ThereExists(ddman, tmp, cvars, num_vars);
|
|
res->nm = (int)DD_GetNumMinterms(ddman, tmp, num_ndvars);
|
|
Cudd_RecursiveDeref(ddman, tmp);
|
|
}
|
|
|
|
// allocate/init arrays
|
|
res->choices = new HDDMatrix*[res->nm];
|
|
res->cubes = new DdNode*[res->nm];
|
|
for (i = 0; i < res->nm; i++) {
|
|
res->choices[i] = NULL;
|
|
res->cubes[i] = NULL;
|
|
}
|
|
|
|
// initialise other fields/stats
|
|
res->compact_sm = false;
|
|
res->num_levels = 0;
|
|
res->l_sm_min = 0;
|
|
res->l_sm_max = 0;
|
|
res->num_nodes = 0;
|
|
res->num_sm = 0;
|
|
res->mem_nodes = 0;
|
|
res->mem_sm = 0;
|
|
|
|
// get cubes to extract sub-mtbdds
|
|
if (existing_mdp != NULL) {
|
|
// just copy existing cubes if appropriate
|
|
for (i = 0; i < res->nm; i++) {
|
|
Cudd_Ref(existing_mdp->cubes[i]);
|
|
res->cubes[i] = existing_mdp->cubes[i];
|
|
}
|
|
} else {
|
|
// otherwise build them
|
|
// (first resetting nm so can use as counter)
|
|
res->nm = 0;
|
|
build_mdp_cubes_rec(mdp, DD_Constant(ddman, 1), rvars, cvars, num_vars, ndvars, num_ndvars, 0, odd, res);
|
|
}
|
|
|
|
// extract sub-mtbdds using cubes
|
|
for (i = 0; i < res->nm; i++) {
|
|
// extract each part of the mtbdd using its cube
|
|
Cudd_Ref(mdp);
|
|
Cudd_Ref(res->cubes[i]);
|
|
tmp = DD_Apply(ddman, APPLY_TIMES, mdp, res->cubes[i]);
|
|
tmp = DD_SumAbstract(ddman, tmp, ndvars, num_ndvars);
|
|
// then build hybrid mtbdd and store it
|
|
hddm = build_hdd_matrix(tmp, rvars, cvars, num_vars, odd, true);
|
|
//printf("%d -> %d\n", DD_GetNumNodes(ddman, tmp), hddm->num_nodes);
|
|
Cudd_RecursiveDeref(ddman, tmp);
|
|
res->choices[i] = hddm;
|
|
// update stats
|
|
res->num_levels = hddm->num_levels;
|
|
res->num_nodes += hddm->num_nodes;
|
|
}
|
|
|
|
// compute memory for nodes
|
|
res->mem_nodes = (res->num_nodes * sizeof(HDDNode)) / 1024.0;
|
|
|
|
// try/catch for memory allocation/deallocation
|
|
} catch(std::bad_alloc e) {
|
|
if (res) delete res;
|
|
throw e;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
void build_mdp_cubes_rec(DdNode *dd, DdNode *cube, DdNode **rvars, DdNode **cvars, int num_vars, DdNode **ndvars, int num_ndvars, int level, ODDNode *odd, HDDMatrices *hddms)
|
|
{
|
|
DdNode *e, *t, *cube_e, *cube_t;
|
|
|
|
// base case - empty choice (matrix)
|
|
if (dd == Cudd_ReadZero(ddman)) {
|
|
Cudd_RecursiveDeref(ddman, cube);
|
|
return;
|
|
}
|
|
|
|
// base case - nonempty choice (matrix)
|
|
if (level == num_ndvars) {
|
|
hddms->cubes[hddms->nm] = cube;
|
|
hddms->nm++;
|
|
return;
|
|
}
|
|
|
|
// recurse
|
|
if (dd->index > ndvars[level]->index) {
|
|
e = t = dd;
|
|
}
|
|
else {
|
|
e = Cudd_E(dd);
|
|
t = Cudd_T(dd);
|
|
}
|
|
Cudd_Ref(cube);
|
|
Cudd_Ref(ndvars[level]);
|
|
cube_e = DD_And(ddman, cube, DD_Not(ddman, ndvars[level]));
|
|
Cudd_Ref(cube);
|
|
Cudd_Ref(ndvars[level]);
|
|
cube_t = DD_And(ddman, cube, ndvars[level]);
|
|
Cudd_RecursiveDeref(ddman, cube);
|
|
|
|
build_mdp_cubes_rec(e, cube_e, rvars, cvars, num_vars, ndvars, num_ndvars, level+1, odd, hddms);
|
|
build_mdp_cubes_rec(t, cube_t, rvars, cvars, num_vars, ndvars, num_ndvars, level+1, odd, hddms);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
// adds sparse matrices on to several hdds (for mdps)
|
|
|
|
void add_sparse_matrices_mdp(HDDMatrices *hddms, bool compact_sm)
|
|
{
|
|
int i;
|
|
|
|
// initialise sparse matrix stats
|
|
hddms->compact_sm = 0;
|
|
hddms->l_sm_min = 0;
|
|
hddms->l_sm_max = 0;
|
|
hddms->num_sm = 0;
|
|
hddms->mem_sm = 0;
|
|
|
|
// add sparse matrices separately for each matrix
|
|
for (i = 0; i < hddms->nm; i++) {
|
|
// add sparse matrix
|
|
add_sparse_matrices(hddms->choices[i], compact_sm, false);
|
|
// update stats
|
|
if (hddms->choices[i]->compact_sm) hddms->compact_sm++;
|
|
if (i == 0) {
|
|
hddms->l_sm_min = hddms->l_sm_max = hddms->choices[i]->l_sm;
|
|
} else {
|
|
if (hddms->choices[i]->l_sm < hddms->l_sm_min) hddms->l_sm_min = hddms->choices[i]->l_sm;
|
|
if (hddms->choices[i]->l_sm > hddms->l_sm_max) hddms->l_sm_max = hddms->choices[i]->l_sm;
|
|
}
|
|
hddms->num_sm += hddms->choices[i]->num_sm;
|
|
hddms->mem_sm += hddms->choices[i]->mem_sm;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
// Methods which manipulate/use offset-labelled MTBBDs
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
// rearrange matrix blocks:
|
|
// 1. put diagonal block at end of row/col (need like that for gauss-seidel)
|
|
// 2. if ooc flag set, put block before diag at start (helps for out-of-core)
|
|
|
|
void rearrange_hdd_blocks(HDDMatrix *hddm, bool ooc)
|
|
{
|
|
int i, j, l, h, rowcol, iminus1;
|
|
|
|
int b_n = hddm->blocks->n;
|
|
HDDNode **b_blocks = hddm->blocks->blocks;
|
|
unsigned int *b_rowscols = hddm->blocks->rowscols;
|
|
unsigned char *b_counts = hddm->blocks->counts;
|
|
int *b_starts = (int *)hddm->blocks->counts;
|
|
bool b_use_counts = hddm->blocks->use_counts;
|
|
int b_dist_shift = hddm->blocks->dist_shift;
|
|
|
|
// go through each row/column of blocks
|
|
h = 0;
|
|
for(i = 0; i < b_n; i++) {
|
|
iminus1 = (i==0)?b_n-1:i-1;
|
|
if (!b_use_counts) { l = b_starts[i]; h = b_starts[i+1]; }
|
|
else { l = h; h += b_counts[i]; }
|
|
// go along this row/column
|
|
for(j = l; j < h; j++) {
|
|
|
|
// get column/row index of block
|
|
if (!hddm->compact_b) {
|
|
rowcol = b_rowscols[j];
|
|
} else {
|
|
rowcol = (unsigned int)(b_rowscols[j] >> b_dist_shift);
|
|
}
|
|
// if it's the diagonal, put it at the end
|
|
if (rowcol == i) {
|
|
unsigned int tmp = b_rowscols[j];
|
|
b_rowscols[j] = b_rowscols[h-1];
|
|
b_rowscols[h-1] = tmp;
|
|
if (!hddm->compact_b) {
|
|
HDDNode *tmp = b_blocks[j];
|
|
b_blocks[j] = b_blocks[h-1];
|
|
b_blocks[h-1] = tmp;
|
|
}
|
|
}
|
|
// if it's the one before the diagonal and the ooc flag is set, put it at the start
|
|
if (rowcol == iminus1) {
|
|
unsigned int tmp = b_rowscols[j];
|
|
b_rowscols[j] = b_rowscols[l];
|
|
b_rowscols[l] = tmp;
|
|
if (!hddm->compact_b) {
|
|
HDDNode *tmp = b_blocks[j];
|
|
b_blocks[j] = b_blocks[l];
|
|
b_blocks[l] = tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
// compute negative sum of elements in each row
|
|
// if transpose flag is true, matrix was previously transposed so,
|
|
// where sparse matrix based storage used (top-level block storage
|
|
// and bottom-level submatrix storage), sum for columns instead
|
|
|
|
// throws std::bad_alloc on out-of-memory
|
|
|
|
double *hdd_negative_row_sums(HDDMatrix *hddm, int n)
|
|
{ return hdd_negative_row_sums(hddm, n, false); }
|
|
|
|
double *hdd_negative_row_sums(HDDMatrix *hddm, int n, bool transpose)
|
|
{
|
|
int i, j, l, h;
|
|
double *diags = NULL;
|
|
bool compact_b = hddm->compact_b;
|
|
compact_sm = hddm->compact_sm;
|
|
row_major = hddm->row_major;
|
|
if (compact_sm) {
|
|
sm_dist = hddm->dist;
|
|
sm_dist_shift = hddm->dist_shift;
|
|
sm_dist_mask = hddm->dist_mask;
|
|
}
|
|
zero = hddm->zero;
|
|
num_levels = hddm->num_levels;
|
|
|
|
// allocate/initialise array
|
|
diags = new double[n];
|
|
for (int i = 0; i < n; i++) diags[i] = 0;
|
|
|
|
// if the matrix hasn't been split into blocks, jump straight to traversal
|
|
if (!hddm->blocks) {
|
|
hdd_negative_row_sums_rec(hddm->top, 0, 0, 0, diags, transpose);
|
|
return diags;
|
|
}
|
|
|
|
// stuff for block storage
|
|
int b_n = hddm->blocks->n;
|
|
HDDNode **b_blocks = hddm->blocks->blocks;
|
|
unsigned int *b_rowscols = hddm->blocks->rowscols;
|
|
unsigned char *b_counts = hddm->blocks->counts;
|
|
int *b_starts = (int *)hddm->blocks->counts;
|
|
bool b_use_counts = hddm->blocks->use_counts;
|
|
int *b_offsets = hddm->blocks->offsets;
|
|
HDDNode **b_nodes = hddm->row_tables[hddm->l_b];
|
|
int b_dist_shift = hddm->blocks->dist_shift;
|
|
int b_dist_mask = hddm->blocks->dist_mask;
|
|
int row_offset;
|
|
int col_offset;
|
|
HDDNode *node;
|
|
|
|
// TODO: it looks like col_offset is not actually used during the various
|
|
// recursion steps (including swapping when transpose is set);
|
|
// candidate for removal / refactoring
|
|
// initialise col_offset
|
|
col_offset = 0;
|
|
|
|
// loop through rows/columns of blocks
|
|
h = 0;
|
|
for (i = 0; i < b_n; i++) {
|
|
|
|
// loop through blocks in this row/column of blocks
|
|
if (!b_use_counts) { l = b_starts[i]; h = b_starts[i+1]; }
|
|
else { l = h; h += b_counts[i]; }
|
|
for (j = l; j < h; j++) {
|
|
|
|
// get node for block and its row offset
|
|
if (!compact_b) {
|
|
node = b_blocks[j];
|
|
row_offset = b_offsets[b_rowscols[j]];
|
|
} else {
|
|
node = b_nodes[(int)(b_rowscols[j] & b_dist_mask)];
|
|
row_offset = b_offsets[(int)(b_rowscols[j] >> b_dist_shift)];
|
|
}
|
|
|
|
// recursively traverse block
|
|
hdd_negative_row_sums_rec(node, hddm->l_b, row_offset, col_offset, diags, transpose);
|
|
}
|
|
}
|
|
|
|
return diags;
|
|
}
|
|
|
|
void hdd_negative_row_sums_rec(HDDNode *hdd, int level, int row_offset, int col_offset, double *diags, bool transpose)
|
|
{
|
|
HDDNode *e, *t;
|
|
|
|
// if it's the zero node
|
|
if (hdd == zero) {
|
|
return;
|
|
}
|
|
// or if we've reached a submatrix
|
|
// (check for non-null ptr but, equivalently, we could just check if level==l_sm)
|
|
else if (hdd->sm.ptr) {
|
|
if (row_major) {
|
|
if (!compact_sm) {
|
|
hdd_negative_row_sums_rm((RMSparseMatrix *)hdd->sm.ptr, (transpose?col_offset:row_offset), (transpose?row_offset:col_offset), diags, transpose);
|
|
} else {
|
|
hdd_negative_row_sums_cmsr((CMSRSparseMatrix *)hdd->sm.ptr, (transpose?col_offset:row_offset), (transpose?row_offset:col_offset), diags, transpose);
|
|
}
|
|
} else {
|
|
if (!compact_sm) {
|
|
hdd_negative_row_sums_cm((CMSparseMatrix *)hdd->sm.ptr, (transpose?col_offset:row_offset), (transpose?row_offset:col_offset), diags, transpose);
|
|
} else {
|
|
hdd_negative_row_sums_cmsc((CMSCSparseMatrix *)hdd->sm.ptr, (transpose?col_offset:row_offset), (transpose?row_offset:col_offset), diags, transpose);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
// or if we've reached the bottom
|
|
else if (level == num_levels) {
|
|
//printf("(%d,%d)=%f\n", row_offset, col_offset, hdd->type.val);
|
|
diags[row_offset] -= hdd->type.val;
|
|
return;
|
|
}
|
|
// otherwise recurse
|
|
e = hdd->type.kids.e;
|
|
if (e != zero) {
|
|
hdd_negative_row_sums_rec(e->type.kids.e, level+1, row_offset, col_offset, diags, transpose);
|
|
hdd_negative_row_sums_rec(e->type.kids.t, level+1, row_offset, col_offset+e->off.val, diags, transpose);
|
|
}
|
|
t = hdd->type.kids.t;
|
|
if (t != zero) {
|
|
hdd_negative_row_sums_rec(t->type.kids.e, level+1, row_offset+hdd->off.val, col_offset, diags, transpose);
|
|
hdd_negative_row_sums_rec(t->type.kids.t, level+1, row_offset+hdd->off.val, col_offset+t->off.val, diags, transpose);
|
|
}
|
|
}
|
|
|
|
void hdd_negative_row_sums_rm(RMSparseMatrix *rmsm, int row_offset, int col_offset, double *diags, bool transpose)
|
|
{
|
|
int i2, j2, l2, h2;
|
|
int sm_n = rmsm->n;
|
|
int sm_nnz = rmsm->nnz;
|
|
double *sm_non_zeros = rmsm->non_zeros;
|
|
unsigned char *sm_row_counts = rmsm->row_counts;
|
|
int *sm_row_starts = (int *)rmsm->row_counts;
|
|
bool sm_use_counts = rmsm->use_counts;
|
|
unsigned int *sm_cols = rmsm->cols;
|
|
|
|
// loop through rows of submatrix
|
|
l2 = sm_nnz; h2 = 0;
|
|
for (i2 = 0; i2 < sm_n; i2++) {
|
|
|
|
// loop through entries in this row
|
|
if (!sm_use_counts) { l2 = sm_row_starts[i2]; h2 = sm_row_starts[i2+1]; }
|
|
else { l2 = h2; h2 += sm_row_counts[i2]; }
|
|
for (j2 = l2; j2 < h2; j2++) {
|
|
diags[(transpose?col_offset+sm_cols[j2]:row_offset + i2)] -= sm_non_zeros[j2];
|
|
}
|
|
}
|
|
}
|
|
|
|
void hdd_negative_row_sums_cm(CMSparseMatrix *cmsm, int row_offset, int col_offset, double *diags, bool transpose)
|
|
{
|
|
int i2, j2, l2, h2;
|
|
int sm_n = cmsm->n;
|
|
int sm_nnz = cmsm->nnz;
|
|
double *sm_non_zeros = cmsm->non_zeros;
|
|
unsigned char *sm_col_counts = cmsm->col_counts;
|
|
int *sm_col_starts = (int *)cmsm->col_counts;
|
|
bool sm_use_counts = cmsm->use_counts;
|
|
unsigned int *sm_rows = cmsm->rows;
|
|
|
|
// loop through columns of submatrix
|
|
l2 = sm_nnz; h2 = 0;
|
|
for (i2 = 0; i2 < sm_n; i2++) {
|
|
|
|
// loop through entries in this column
|
|
if (!sm_use_counts) { l2 = sm_col_starts[i2]; h2 = sm_col_starts[i2+1]; }
|
|
else { l2 = h2; h2 += sm_col_counts[i2]; }
|
|
for (j2 = l2; j2 < h2; j2++) {
|
|
diags[(transpose?col_offset + i2:row_offset + sm_rows[j2])] -= sm_non_zeros[j2];
|
|
}
|
|
}
|
|
}
|
|
|
|
void hdd_negative_row_sums_cmsr(CMSRSparseMatrix *cmsrsm, int row_offset, int col_offset, double *diags, bool transpose)
|
|
{
|
|
int i2, j2, l2, h2;
|
|
int sm_n = cmsrsm->n;
|
|
int sm_nnz = cmsrsm->nnz;
|
|
unsigned char *sm_row_counts = cmsrsm->row_counts;
|
|
int *sm_row_starts = (int *)cmsrsm->row_counts;
|
|
bool sm_use_counts = cmsrsm->use_counts;
|
|
unsigned int *sm_cols = cmsrsm->cols;
|
|
|
|
// loop through rows of submatrix
|
|
l2 = sm_nnz; h2 = 0;
|
|
for (i2 = 0; i2 < sm_n; i2++) {
|
|
|
|
// loop through entries in this row
|
|
if (!sm_use_counts) { l2 = sm_row_starts[i2]; h2 = sm_row_starts[i2+1]; }
|
|
else { l2 = h2; h2 += sm_row_counts[i2]; }
|
|
for (j2 = l2; j2 < h2; j2++) {
|
|
diags[(transpose?col_offset + ((int)(sm_cols[j2] >> sm_dist_shift)):row_offset + i2)] -= sm_dist[(int)(sm_cols[j2] & sm_dist_mask)];
|
|
}
|
|
}
|
|
}
|
|
|
|
void hdd_negative_row_sums_cmsc(CMSCSparseMatrix *cmscsm, int row_offset, int col_offset, double *diags, bool transpose)
|
|
{
|
|
int i2, j2, l2, h2;
|
|
int sm_n = cmscsm->n;
|
|
int sm_nnz = cmscsm->nnz;
|
|
unsigned char *sm_col_counts = cmscsm->col_counts;
|
|
int *sm_col_starts = (int *)cmscsm->col_counts;
|
|
bool sm_use_counts = cmscsm->use_counts;
|
|
unsigned int *sm_rows = cmscsm->rows;
|
|
|
|
// loop through columns of submatrix
|
|
l2 = sm_nnz; h2 = 0;
|
|
for (i2 = 0; i2 < sm_n; i2++) {
|
|
|
|
// loop through entries in this column
|
|
if (!sm_use_counts) { l2 = sm_col_starts[i2]; h2 = sm_col_starts[i2+1]; }
|
|
else { l2 = h2; h2 += sm_col_counts[i2]; }
|
|
for (j2 = l2; j2 < h2; j2++) {
|
|
diags[(transpose?col_offset + i2:row_offset + ((int)(sm_rows[j2] >> sm_dist_shift)))] -= sm_dist[(int)(sm_rows[j2] & sm_dist_mask)];
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|