1396 lines
34 KiB
C++
1396 lines
34 KiB
C++
//===---------------- PBQP.cpp --------- PBQP Solver ------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Developed by: Bernhard Scholz
|
|
// The University of Sydney
|
|
// http://www.it.usyd.edu.au/~scholz
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "PBQP.h"
|
|
#include "llvm/Config/alloca.h"
|
|
#include <limits>
|
|
#include <cassert>
|
|
#include <cstring>
|
|
|
|
namespace llvm {
|
|
|
|
/**************************************************************************
|
|
* Data Structures
|
|
**************************************************************************/
|
|
|
|
/* edge of PBQP graph */
|
|
typedef struct adjnode {
|
|
struct adjnode *prev, /* doubly chained list */
|
|
*succ,
|
|
*reverse; /* reverse edge */
|
|
int adj; /* adj. node */
|
|
PBQPMatrix *costs; /* cost matrix of edge */
|
|
|
|
bool tc_valid; /* flag whether following fields are valid */
|
|
int *tc_safe_regs; /* safe registers */
|
|
int tc_impact; /* impact */
|
|
} adjnode;
|
|
|
|
/* bucket node */
|
|
typedef struct bucketnode {
|
|
struct bucketnode *prev; /* doubly chained list */
|
|
struct bucketnode *succ;
|
|
int u; /* node */
|
|
} bucketnode;
|
|
|
|
/* data structure of partitioned boolean quadratic problem */
|
|
struct pbqp {
|
|
int num_nodes; /* number of nodes */
|
|
int max_deg; /* maximal degree of a node */
|
|
bool solved; /* flag that indicates whether PBQP has been solved yet */
|
|
bool optimal; /* flag that indicates whether PBQP is optimal */
|
|
PBQPNum min;
|
|
bool changed; /* flag whether graph has changed in simplification */
|
|
|
|
/* node fields */
|
|
PBQPVector **node_costs; /* cost vectors of nodes */
|
|
int *node_deg; /* node degree of nodes */
|
|
int *solution; /* solution for node */
|
|
adjnode **adj_list; /* adj. list */
|
|
bucketnode **bucket_ptr; /* bucket pointer of a node */
|
|
|
|
/* node stack */
|
|
int *stack; /* stack of nodes */
|
|
int stack_ptr; /* stack pointer */
|
|
|
|
/* bucket fields */
|
|
bucketnode **bucket_list; /* bucket list */
|
|
|
|
int num_r0; /* counters for number statistics */
|
|
int num_ri;
|
|
int num_rii;
|
|
int num_rn;
|
|
int num_rn_special;
|
|
};
|
|
|
|
bool isInf(PBQPNum n) { return n == std::numeric_limits<PBQPNum>::infinity(); }
|
|
|
|
/*****************************************************************************
|
|
* allocation/de-allocation of pbqp problem
|
|
****************************************************************************/
|
|
|
|
/* allocate new partitioned boolean quadratic program problem */
|
|
pbqp *alloc_pbqp(int num_nodes)
|
|
{
|
|
pbqp *this_;
|
|
int u;
|
|
|
|
assert(num_nodes > 0);
|
|
|
|
/* allocate memory for pbqp data structure */
|
|
this_ = (pbqp *)malloc(sizeof(pbqp));
|
|
|
|
/* Initialize pbqp fields */
|
|
this_->num_nodes = num_nodes;
|
|
this_->solved = false;
|
|
this_->optimal = true;
|
|
this_->min = 0.0;
|
|
this_->max_deg = 0;
|
|
this_->changed = false;
|
|
this_->num_r0 = 0;
|
|
this_->num_ri = 0;
|
|
this_->num_rii = 0;
|
|
this_->num_rn = 0;
|
|
this_->num_rn_special = 0;
|
|
|
|
/* initialize/allocate stack fields of pbqp */
|
|
this_->stack = (int *) malloc(sizeof(int)*num_nodes);
|
|
this_->stack_ptr = 0;
|
|
|
|
/* initialize/allocate node fields of pbqp */
|
|
this_->adj_list = (adjnode **) malloc(sizeof(adjnode *)*num_nodes);
|
|
this_->node_deg = (int *) malloc(sizeof(int)*num_nodes);
|
|
this_->solution = (int *) malloc(sizeof(int)*num_nodes);
|
|
this_->bucket_ptr = (bucketnode **) malloc(sizeof(bucketnode **)*num_nodes);
|
|
this_->node_costs = (PBQPVector**) malloc(sizeof(PBQPVector*) * num_nodes);
|
|
for(u=0;u<num_nodes;u++) {
|
|
this_->solution[u]=-1;
|
|
this_->adj_list[u]=NULL;
|
|
this_->node_deg[u]=0;
|
|
this_->bucket_ptr[u]=NULL;
|
|
this_->node_costs[u]=NULL;
|
|
}
|
|
|
|
/* initialize bucket list */
|
|
this_->bucket_list = NULL;
|
|
|
|
return this_;
|
|
}
|
|
|
|
/* free pbqp problem */
|
|
void free_pbqp(pbqp *this_)
|
|
{
|
|
int u;
|
|
int deg;
|
|
adjnode *adj_ptr,*adj_next;
|
|
bucketnode *bucket,*bucket_next;
|
|
|
|
assert(this_ != NULL);
|
|
|
|
/* free node cost fields */
|
|
for(u=0;u < this_->num_nodes;u++) {
|
|
delete this_->node_costs[u];
|
|
}
|
|
free(this_->node_costs);
|
|
|
|
/* free bucket list */
|
|
for(deg=0;deg<=this_->max_deg;deg++) {
|
|
for(bucket=this_->bucket_list[deg];bucket!=NULL;bucket=bucket_next) {
|
|
this_->bucket_ptr[bucket->u] = NULL;
|
|
bucket_next = bucket-> succ;
|
|
free(bucket);
|
|
}
|
|
}
|
|
free(this_->bucket_list);
|
|
|
|
/* free adj. list */
|
|
assert(this_->adj_list != NULL);
|
|
for(u=0;u < this_->num_nodes; u++) {
|
|
for(adj_ptr = this_->adj_list[u]; adj_ptr != NULL; adj_ptr = adj_next) {
|
|
adj_next = adj_ptr -> succ;
|
|
if (u < adj_ptr->adj) {
|
|
assert(adj_ptr != NULL);
|
|
delete adj_ptr->costs;
|
|
}
|
|
if (adj_ptr -> tc_safe_regs != NULL) {
|
|
free(adj_ptr -> tc_safe_regs);
|
|
}
|
|
free(adj_ptr);
|
|
}
|
|
}
|
|
free(this_->adj_list);
|
|
|
|
/* free other node fields */
|
|
free(this_->node_deg);
|
|
free(this_->solution);
|
|
free(this_->bucket_ptr);
|
|
|
|
/* free stack */
|
|
free(this_->stack);
|
|
|
|
/* free pbqp data structure itself */
|
|
free(this_);
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* adj. node routines
|
|
****************************************************************************/
|
|
|
|
/* find data structure of adj. node of a given node */
|
|
static
|
|
adjnode *find_adjnode(pbqp *this_,int u,int v)
|
|
{
|
|
adjnode *adj_ptr;
|
|
|
|
assert (this_ != NULL);
|
|
assert (u >= 0 && u < this_->num_nodes);
|
|
assert (v >= 0 && v < this_->num_nodes);
|
|
assert(this_->adj_list != NULL);
|
|
|
|
for(adj_ptr = this_ -> adj_list[u];adj_ptr != NULL; adj_ptr = adj_ptr -> succ) {
|
|
if (adj_ptr->adj == v) {
|
|
return adj_ptr;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* allocate a new data structure for adj. node */
|
|
static
|
|
adjnode *alloc_adjnode(pbqp *this_,int u, PBQPMatrix *costs)
|
|
{
|
|
adjnode *p;
|
|
|
|
assert(this_ != NULL);
|
|
assert(costs != NULL);
|
|
assert(u >= 0 && u < this_->num_nodes);
|
|
|
|
p = (adjnode *)malloc(sizeof(adjnode));
|
|
assert(p != NULL);
|
|
|
|
p->adj = u;
|
|
p->costs = costs;
|
|
|
|
p->tc_valid= false;
|
|
p->tc_safe_regs = NULL;
|
|
p->tc_impact = 0;
|
|
|
|
return p;
|
|
}
|
|
|
|
/* insert adjacence node to adj. list */
|
|
static
|
|
void insert_adjnode(pbqp *this_, int u, adjnode *adj_ptr)
|
|
{
|
|
|
|
assert(this_ != NULL);
|
|
assert(adj_ptr != NULL);
|
|
assert(u >= 0 && u < this_->num_nodes);
|
|
|
|
/* if adjacency list of node is not empty -> update
|
|
first node of the list */
|
|
if (this_ -> adj_list[u] != NULL) {
|
|
assert(this_->adj_list[u]->prev == NULL);
|
|
this_->adj_list[u] -> prev = adj_ptr;
|
|
}
|
|
|
|
/* update doubly chained list pointers of pointers */
|
|
adj_ptr -> succ = this_->adj_list[u];
|
|
adj_ptr -> prev = NULL;
|
|
|
|
/* update adjacency list pointer of node u */
|
|
this_->adj_list[u] = adj_ptr;
|
|
}
|
|
|
|
/* remove entry in an adj. list */
|
|
static
|
|
void remove_adjnode(pbqp *this_, int u, adjnode *adj_ptr)
|
|
{
|
|
assert(this_!= NULL);
|
|
assert(u >= 0 && u <= this_->num_nodes);
|
|
assert(this_->adj_list != NULL);
|
|
assert(adj_ptr != NULL);
|
|
|
|
if (adj_ptr -> prev == NULL) {
|
|
this_->adj_list[u] = adj_ptr -> succ;
|
|
} else {
|
|
adj_ptr -> prev -> succ = adj_ptr -> succ;
|
|
}
|
|
|
|
if (adj_ptr -> succ != NULL) {
|
|
adj_ptr -> succ -> prev = adj_ptr -> prev;
|
|
}
|
|
|
|
if(adj_ptr->reverse != NULL) {
|
|
adjnode *rev = adj_ptr->reverse;
|
|
rev->reverse = NULL;
|
|
}
|
|
|
|
if (adj_ptr -> tc_safe_regs != NULL) {
|
|
free(adj_ptr -> tc_safe_regs);
|
|
}
|
|
|
|
free(adj_ptr);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* node functions
|
|
****************************************************************************/
|
|
|
|
/* get degree of a node */
|
|
static
|
|
int get_deg(pbqp *this_,int u)
|
|
{
|
|
adjnode *adj_ptr;
|
|
int deg = 0;
|
|
|
|
assert(this_ != NULL);
|
|
assert(u >= 0 && u < this_->num_nodes);
|
|
assert(this_->adj_list != NULL);
|
|
|
|
for(adj_ptr = this_ -> adj_list[u];adj_ptr != NULL; adj_ptr = adj_ptr -> succ) {
|
|
deg ++;
|
|
}
|
|
return deg;
|
|
}
|
|
|
|
/* reinsert node */
|
|
static
|
|
void reinsert_node(pbqp *this_,int u)
|
|
{
|
|
adjnode *adj_u,
|
|
*adj_v;
|
|
|
|
assert(this_!= NULL);
|
|
assert(u >= 0 && u <= this_->num_nodes);
|
|
assert(this_->adj_list != NULL);
|
|
|
|
for(adj_u = this_ -> adj_list[u]; adj_u != NULL; adj_u = adj_u -> succ) {
|
|
int v = adj_u -> adj;
|
|
adj_v = alloc_adjnode(this_,u,adj_u->costs);
|
|
insert_adjnode(this_,v,adj_v);
|
|
}
|
|
}
|
|
|
|
/* remove node */
|
|
static
|
|
void remove_node(pbqp *this_,int u)
|
|
{
|
|
adjnode *adj_ptr;
|
|
|
|
assert(this_!= NULL);
|
|
assert(u >= 0 && u <= this_->num_nodes);
|
|
assert(this_->adj_list != NULL);
|
|
|
|
for(adj_ptr = this_ -> adj_list[u]; adj_ptr != NULL; adj_ptr = adj_ptr -> succ) {
|
|
remove_adjnode(this_,adj_ptr->adj,adj_ptr -> reverse);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* edge functions
|
|
****************************************************************************/
|
|
|
|
/* insert edge to graph */
|
|
/* (does not check whether edge exists in graph */
|
|
static
|
|
void insert_edge(pbqp *this_, int u, int v, PBQPMatrix *costs)
|
|
{
|
|
adjnode *adj_u,
|
|
*adj_v;
|
|
|
|
/* create adjanceny entry for u */
|
|
adj_u = alloc_adjnode(this_,v,costs);
|
|
insert_adjnode(this_,u,adj_u);
|
|
|
|
|
|
/* create adjanceny entry for v */
|
|
adj_v = alloc_adjnode(this_,u,costs);
|
|
insert_adjnode(this_,v,adj_v);
|
|
|
|
/* create link for reverse edge */
|
|
adj_u -> reverse = adj_v;
|
|
adj_v -> reverse = adj_u;
|
|
}
|
|
|
|
/* delete edge */
|
|
static
|
|
void delete_edge(pbqp *this_,int u,int v)
|
|
{
|
|
adjnode *adj_ptr;
|
|
adjnode *rev;
|
|
|
|
assert(this_ != NULL);
|
|
assert( u >= 0 && u < this_->num_nodes);
|
|
assert( v >= 0 && v < this_->num_nodes);
|
|
|
|
adj_ptr=find_adjnode(this_,u,v);
|
|
assert(adj_ptr != NULL);
|
|
assert(adj_ptr->reverse != NULL);
|
|
|
|
delete adj_ptr -> costs;
|
|
|
|
rev = adj_ptr->reverse;
|
|
remove_adjnode(this_,u,adj_ptr);
|
|
remove_adjnode(this_,v,rev);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* cost functions
|
|
****************************************************************************/
|
|
|
|
/* Note: Since cost(u,v) = transpose(cost(v,u)), it would be necessary to store
|
|
two matrices for both edges (u,v) and (v,u). However, we only store the
|
|
matrix for the case u < v. For the other case we transpose the stored matrix
|
|
if required.
|
|
*/
|
|
|
|
/* add costs to cost vector of a node */
|
|
void add_pbqp_nodecosts(pbqp *this_,int u, PBQPVector *costs)
|
|
{
|
|
assert(this_ != NULL);
|
|
assert(costs != NULL);
|
|
assert(u >= 0 && u <= this_->num_nodes);
|
|
|
|
if (!this_->node_costs[u]) {
|
|
this_->node_costs[u] = new PBQPVector(*costs);
|
|
} else {
|
|
*this_->node_costs[u] += *costs;
|
|
}
|
|
}
|
|
|
|
/* get cost matrix ptr */
|
|
static
|
|
PBQPMatrix *get_costmatrix_ptr(pbqp *this_, int u, int v)
|
|
{
|
|
adjnode *adj_ptr;
|
|
PBQPMatrix *m = NULL;
|
|
|
|
assert (this_ != NULL);
|
|
assert (u >= 0 && u < this_->num_nodes);
|
|
assert (v >= 0 && v < this_->num_nodes);
|
|
|
|
adj_ptr = find_adjnode(this_,u,v);
|
|
|
|
if (adj_ptr != NULL) {
|
|
m = adj_ptr -> costs;
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
/* get cost matrix ptr */
|
|
/* Note: only the pointer is returned for
|
|
cost(u,v), if u < v.
|
|
*/
|
|
static
|
|
PBQPMatrix *pbqp_get_costmatrix(pbqp *this_, int u, int v)
|
|
{
|
|
adjnode *adj_ptr = find_adjnode(this_,u,v);
|
|
|
|
if (adj_ptr != NULL) {
|
|
if ( u < v) {
|
|
return new PBQPMatrix(*adj_ptr->costs);
|
|
} else {
|
|
return new PBQPMatrix(adj_ptr->costs->transpose());
|
|
}
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* add costs to cost matrix of an edge */
|
|
void add_pbqp_edgecosts(pbqp *this_,int u,int v, PBQPMatrix *costs)
|
|
{
|
|
PBQPMatrix *adj_costs;
|
|
|
|
assert(this_!= NULL);
|
|
assert(costs != NULL);
|
|
assert(u >= 0 && u <= this_->num_nodes);
|
|
assert(v >= 0 && v <= this_->num_nodes);
|
|
|
|
/* does the edge u-v exists ? */
|
|
if (u == v) {
|
|
PBQPVector *diag = new PBQPVector(costs->diagonalize());
|
|
add_pbqp_nodecosts(this_,v,diag);
|
|
delete diag;
|
|
} else if ((adj_costs = get_costmatrix_ptr(this_,u,v))!=NULL) {
|
|
if ( u < v) {
|
|
*adj_costs += *costs;
|
|
} else {
|
|
*adj_costs += costs->transpose();
|
|
}
|
|
} else {
|
|
adj_costs = new PBQPMatrix((u < v) ? *costs : costs->transpose());
|
|
insert_edge(this_,u,v,adj_costs);
|
|
}
|
|
}
|
|
|
|
/* remove bucket from bucket list */
|
|
static
|
|
void pbqp_remove_bucket(pbqp *this_, bucketnode *bucket)
|
|
{
|
|
int u = bucket->u;
|
|
|
|
assert(this_ != NULL);
|
|
assert(u >= 0 && u < this_->num_nodes);
|
|
assert(this_->bucket_list != NULL);
|
|
assert(this_->bucket_ptr[u] != NULL);
|
|
|
|
/* update predecessor node in bucket list
|
|
(if no preceeding bucket exists, then
|
|
the bucket_list pointer needs to be
|
|
updated.)
|
|
*/
|
|
if (bucket->prev != NULL) {
|
|
bucket->prev-> succ = bucket->succ;
|
|
} else {
|
|
this_->bucket_list[this_->node_deg[u]] = bucket -> succ;
|
|
}
|
|
|
|
/* update successor node in bucket list */
|
|
if (bucket->succ != NULL) {
|
|
bucket->succ-> prev = bucket->prev;
|
|
}
|
|
}
|
|
|
|
/**********************************************************************************
|
|
* pop functions
|
|
**********************************************************************************/
|
|
|
|
/* pop node of given degree */
|
|
static
|
|
int pop_node(pbqp *this_,int deg)
|
|
{
|
|
bucketnode *bucket;
|
|
int u;
|
|
|
|
assert(this_ != NULL);
|
|
assert(deg >= 0 && deg <= this_->max_deg);
|
|
assert(this_->bucket_list != NULL);
|
|
|
|
/* get first bucket of bucket list */
|
|
bucket = this_->bucket_list[deg];
|
|
assert(bucket != NULL);
|
|
|
|
/* remove bucket */
|
|
pbqp_remove_bucket(this_,bucket);
|
|
u = bucket->u;
|
|
free(bucket);
|
|
return u;
|
|
}
|
|
|
|
/**********************************************************************************
|
|
* reorder functions
|
|
**********************************************************************************/
|
|
|
|
/* add bucket to bucketlist */
|
|
static
|
|
void add_to_bucketlist(pbqp *this_,bucketnode *bucket, int deg)
|
|
{
|
|
bucketnode *old_head;
|
|
|
|
assert(bucket != NULL);
|
|
assert(this_ != NULL);
|
|
assert(deg >= 0 && deg <= this_->max_deg);
|
|
assert(this_->bucket_list != NULL);
|
|
|
|
/* store node degree (for re-ordering purposes)*/
|
|
this_->node_deg[bucket->u] = deg;
|
|
|
|
/* put bucket to front of doubly chained list */
|
|
old_head = this_->bucket_list[deg];
|
|
bucket -> prev = NULL;
|
|
bucket -> succ = old_head;
|
|
this_ -> bucket_list[deg] = bucket;
|
|
if (bucket -> succ != NULL ) {
|
|
assert ( old_head -> prev == NULL);
|
|
old_head -> prev = bucket;
|
|
}
|
|
}
|
|
|
|
|
|
/* reorder node in bucket list according to
|
|
current node degree */
|
|
static
|
|
void reorder_node(pbqp *this_, int u)
|
|
{
|
|
int deg;
|
|
|
|
assert(this_ != NULL);
|
|
assert(u>= 0 && u < this_->num_nodes);
|
|
assert(this_->bucket_list != NULL);
|
|
assert(this_->bucket_ptr[u] != NULL);
|
|
|
|
/* get current node degree */
|
|
deg = get_deg(this_,u);
|
|
|
|
/* remove bucket from old bucket list only
|
|
if degree of node has changed. */
|
|
if (deg != this_->node_deg[u]) {
|
|
pbqp_remove_bucket(this_,this_->bucket_ptr[u]);
|
|
add_to_bucketlist(this_,this_->bucket_ptr[u],deg);
|
|
}
|
|
}
|
|
|
|
/* reorder adj. nodes of a node */
|
|
static
|
|
void reorder_adjnodes(pbqp *this_,int u)
|
|
{
|
|
adjnode *adj_ptr;
|
|
|
|
assert(this_!= NULL);
|
|
assert(u >= 0 && u <= this_->num_nodes);
|
|
assert(this_->adj_list != NULL);
|
|
|
|
for(adj_ptr = this_ -> adj_list[u]; adj_ptr != NULL; adj_ptr = adj_ptr -> succ) {
|
|
reorder_node(this_,adj_ptr->adj);
|
|
}
|
|
}
|
|
|
|
/**********************************************************************************
|
|
* creation functions
|
|
**********************************************************************************/
|
|
|
|
/* create new bucket entry */
|
|
/* consistency of the bucket list is not checked! */
|
|
static
|
|
void create_bucket(pbqp *this_,int u,int deg)
|
|
{
|
|
bucketnode *bucket;
|
|
|
|
assert(this_ != NULL);
|
|
assert(u >= 0 && u < this_->num_nodes);
|
|
assert(this_->bucket_list != NULL);
|
|
|
|
bucket = (bucketnode *)malloc(sizeof(bucketnode));
|
|
assert(bucket != NULL);
|
|
|
|
bucket -> u = u;
|
|
this_->bucket_ptr[u] = bucket;
|
|
|
|
add_to_bucketlist(this_,bucket,deg);
|
|
}
|
|
|
|
/* create bucket list */
|
|
static
|
|
void create_bucketlist(pbqp *this_)
|
|
{
|
|
int u;
|
|
int max_deg;
|
|
int deg;
|
|
|
|
assert(this_ != NULL);
|
|
assert(this_->bucket_list == NULL);
|
|
|
|
/* determine max. degree of the nodes */
|
|
max_deg = 2; /* at least of degree two! */
|
|
for(u=0;u<this_->num_nodes;u++) {
|
|
deg = this_->node_deg[u] = get_deg(this_,u);
|
|
if (deg > max_deg) {
|
|
max_deg = deg;
|
|
}
|
|
}
|
|
this_->max_deg = max_deg;
|
|
|
|
/* allocate bucket list */
|
|
this_ -> bucket_list = (bucketnode **)malloc(sizeof(bucketnode *)*(max_deg + 1));
|
|
memset(this_->bucket_list,0,sizeof(bucketnode *)*(max_deg + 1));
|
|
assert(this_->bucket_list != NULL);
|
|
|
|
/* insert nodes to the list */
|
|
for(u=0;u<this_->num_nodes;u++) {
|
|
create_bucket(this_,u,this_->node_deg[u]);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PBQP simplification for trivial nodes
|
|
****************************************************************************/
|
|
|
|
/* remove trivial node with cost vector length of one */
|
|
static
|
|
void disconnect_trivialnode(pbqp *this_,int u)
|
|
{
|
|
int v;
|
|
adjnode *adj_ptr,
|
|
*next;
|
|
PBQPMatrix *c_uv;
|
|
PBQPVector *c_v;
|
|
|
|
assert(this_ != NULL);
|
|
assert(this_->node_costs != NULL);
|
|
assert(u >= 0 && u < this_ -> num_nodes);
|
|
assert(this_->node_costs[u]->getLength() == 1);
|
|
|
|
/* add edge costs to node costs of adj. nodes */
|
|
for(adj_ptr = this_->adj_list[u]; adj_ptr != NULL; adj_ptr = next){
|
|
next = adj_ptr -> succ;
|
|
v = adj_ptr -> adj;
|
|
assert(v >= 0 && v < this_ -> num_nodes);
|
|
|
|
/* convert matrix to cost vector offset for adj. node */
|
|
c_uv = pbqp_get_costmatrix(this_,u,v);
|
|
c_v = new PBQPVector(c_uv->getRowAsVector(0));
|
|
*this_->node_costs[v] += *c_v;
|
|
|
|
/* delete edge & free vec/mat */
|
|
delete c_v;
|
|
delete c_uv;
|
|
delete_edge(this_,u,v);
|
|
}
|
|
}
|
|
|
|
/* find all trivial nodes and disconnect them */
|
|
static
|
|
void eliminate_trivial_nodes(pbqp *this_)
|
|
{
|
|
int u;
|
|
|
|
assert(this_ != NULL);
|
|
assert(this_ -> node_costs != NULL);
|
|
|
|
for(u=0;u < this_ -> num_nodes; u++) {
|
|
if (this_->node_costs[u]->getLength() == 1) {
|
|
disconnect_trivialnode(this_,u);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Normal form for PBQP
|
|
****************************************************************************/
|
|
|
|
/* simplify a cost matrix. If the matrix
|
|
is independent, then simplify_matrix
|
|
returns true - otherwise false. In
|
|
vectors u and v the offset values of
|
|
the decomposition are stored.
|
|
*/
|
|
|
|
static
|
|
bool normalize_matrix(PBQPMatrix *m, PBQPVector *u, PBQPVector *v)
|
|
{
|
|
assert( m != NULL);
|
|
assert( u != NULL);
|
|
assert( v != NULL);
|
|
assert( u->getLength() > 0);
|
|
assert( v->getLength() > 0);
|
|
|
|
assert(m->getRows() == u->getLength());
|
|
assert(m->getCols() == v->getLength());
|
|
|
|
/* determine u vector */
|
|
for(unsigned r = 0; r < m->getRows(); ++r) {
|
|
PBQPNum min = m->getRowMin(r);
|
|
(*u)[r] += min;
|
|
if (!isInf(min)) {
|
|
m->subFromRow(r, min);
|
|
} else {
|
|
m->setRow(r, 0);
|
|
}
|
|
}
|
|
|
|
/* determine v vector */
|
|
for(unsigned c = 0; c < m->getCols(); ++c) {
|
|
PBQPNum min = m->getColMin(c);
|
|
(*v)[c] += min;
|
|
if (!isInf(min)) {
|
|
m->subFromCol(c, min);
|
|
} else {
|
|
m->setCol(c, 0);
|
|
}
|
|
}
|
|
|
|
/* determine whether matrix is
|
|
independent or not.
|
|
*/
|
|
return m->isZero();
|
|
}
|
|
|
|
/* simplify single edge */
|
|
static
|
|
void simplify_edge(pbqp *this_,int u,int v)
|
|
{
|
|
PBQPMatrix *costs;
|
|
bool is_zero;
|
|
|
|
assert (this_ != NULL);
|
|
assert (u >= 0 && u <this_->num_nodes);
|
|
assert (v >= 0 && v <this_->num_nodes);
|
|
assert (u != v);
|
|
|
|
/* swap u and v if u > v in order to avoid un-necessary
|
|
tranpositions of the cost matrix */
|
|
|
|
if (u > v) {
|
|
int swap = u;
|
|
u = v;
|
|
v = swap;
|
|
}
|
|
|
|
/* get cost matrix and simplify it */
|
|
costs = get_costmatrix_ptr(this_,u,v);
|
|
is_zero=normalize_matrix(costs,this_->node_costs[u],this_->node_costs[v]);
|
|
|
|
/* delete edge */
|
|
if(is_zero){
|
|
delete_edge(this_,u,v);
|
|
this_->changed = true;
|
|
}
|
|
}
|
|
|
|
/* normalize cost matrices and remove
|
|
edges in PBQP if they ary independent,
|
|
i.e. can be decomposed into two
|
|
cost vectors.
|
|
*/
|
|
static
|
|
void eliminate_independent_edges(pbqp *this_)
|
|
{
|
|
int u,v;
|
|
adjnode *adj_ptr,*next;
|
|
|
|
assert(this_ != NULL);
|
|
assert(this_ -> adj_list != NULL);
|
|
|
|
this_->changed = false;
|
|
for(u=0;u < this_->num_nodes;u++) {
|
|
for (adj_ptr = this_ -> adj_list[u]; adj_ptr != NULL; adj_ptr = next) {
|
|
next = adj_ptr -> succ;
|
|
v = adj_ptr -> adj;
|
|
assert(v >= 0 && v < this_->num_nodes);
|
|
if (u < v) {
|
|
simplify_edge(this_,u,v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* PBQP reduction rules
|
|
****************************************************************************/
|
|
|
|
/* RI reduction
|
|
This reduction rule is applied for nodes
|
|
of degree one. */
|
|
|
|
static
|
|
void apply_RI(pbqp *this_,int x)
|
|
{
|
|
int y;
|
|
unsigned xlen,
|
|
ylen;
|
|
PBQPMatrix *c_yx;
|
|
PBQPVector *c_x, *delta;
|
|
|
|
assert(this_ != NULL);
|
|
assert(x >= 0 && x < this_->num_nodes);
|
|
assert(this_ -> adj_list[x] != NULL);
|
|
assert(this_ -> adj_list[x] -> succ == NULL);
|
|
|
|
/* get adjacence matrix */
|
|
y = this_ -> adj_list[x] -> adj;
|
|
assert(y >= 0 && y < this_->num_nodes);
|
|
|
|
/* determine length of cost vectors for node x and y */
|
|
xlen = this_ -> node_costs[x]->getLength();
|
|
ylen = this_ -> node_costs[y]->getLength();
|
|
|
|
/* get cost vector c_x and matrix c_yx */
|
|
c_x = this_ -> node_costs[x];
|
|
c_yx = pbqp_get_costmatrix(this_,y,x);
|
|
assert (c_yx != NULL);
|
|
|
|
|
|
/* allocate delta vector */
|
|
delta = new PBQPVector(ylen);
|
|
|
|
/* compute delta vector */
|
|
for(unsigned i = 0; i < ylen; ++i) {
|
|
PBQPNum min = (*c_yx)[i][0] + (*c_x)[0];
|
|
for(unsigned j = 1; j < xlen; ++j) {
|
|
PBQPNum c = (*c_yx)[i][j] + (*c_x)[j];
|
|
if ( c < min )
|
|
min = c;
|
|
}
|
|
(*delta)[i] = min;
|
|
}
|
|
|
|
/* add delta vector */
|
|
*this_ -> node_costs[y] += *delta;
|
|
|
|
/* delete node x */
|
|
remove_node(this_,x);
|
|
|
|
/* reorder adj. nodes of node x */
|
|
reorder_adjnodes(this_,x);
|
|
|
|
/* push node x on stack */
|
|
assert(this_ -> stack_ptr < this_ -> num_nodes);
|
|
this_->stack[this_ -> stack_ptr++] = x;
|
|
|
|
/* free vec/mat */
|
|
delete c_yx;
|
|
delete delta;
|
|
|
|
/* increment counter for number statistic */
|
|
this_->num_ri++;
|
|
}
|
|
|
|
/* RII reduction
|
|
This reduction rule is applied for nodes
|
|
of degree two. */
|
|
|
|
static
|
|
void apply_RII(pbqp *this_,int x)
|
|
{
|
|
int y,z;
|
|
unsigned xlen,ylen,zlen;
|
|
adjnode *adj_yz;
|
|
|
|
PBQPMatrix *c_yx, *c_zx;
|
|
PBQPVector *cx;
|
|
PBQPMatrix *delta;
|
|
|
|
assert(this_ != NULL);
|
|
assert(x >= 0 && x < this_->num_nodes);
|
|
assert(this_ -> adj_list[x] != NULL);
|
|
assert(this_ -> adj_list[x] -> succ != NULL);
|
|
assert(this_ -> adj_list[x] -> succ -> succ == NULL);
|
|
|
|
/* get adjacence matrix */
|
|
y = this_ -> adj_list[x] -> adj;
|
|
z = this_ -> adj_list[x] -> succ -> adj;
|
|
assert(y >= 0 && y < this_->num_nodes);
|
|
assert(z >= 0 && z < this_->num_nodes);
|
|
|
|
/* determine length of cost vectors for node x and y */
|
|
xlen = this_ -> node_costs[x]->getLength();
|
|
ylen = this_ -> node_costs[y]->getLength();
|
|
zlen = this_ -> node_costs[z]->getLength();
|
|
|
|
/* get cost vector c_x and matrix c_yx */
|
|
cx = this_ -> node_costs[x];
|
|
c_yx = pbqp_get_costmatrix(this_,y,x);
|
|
c_zx = pbqp_get_costmatrix(this_,z,x);
|
|
assert(c_yx != NULL);
|
|
assert(c_zx != NULL);
|
|
|
|
/* Colour Heuristic */
|
|
if ( (adj_yz = find_adjnode(this_,y,z)) != NULL) {
|
|
adj_yz->tc_valid = false;
|
|
adj_yz->reverse->tc_valid = false;
|
|
}
|
|
|
|
/* allocate delta matrix */
|
|
delta = new PBQPMatrix(ylen, zlen);
|
|
|
|
/* compute delta matrix */
|
|
for(unsigned i=0;i<ylen;i++) {
|
|
for(unsigned j=0;j<zlen;j++) {
|
|
PBQPNum min = (*c_yx)[i][0] + (*c_zx)[j][0] + (*cx)[0];
|
|
for(unsigned k=1;k<xlen;k++) {
|
|
PBQPNum c = (*c_yx)[i][k] + (*c_zx)[j][k] + (*cx)[k];
|
|
if ( c < min ) {
|
|
min = c;
|
|
}
|
|
}
|
|
(*delta)[i][j] = min;
|
|
}
|
|
}
|
|
|
|
/* add delta matrix */
|
|
add_pbqp_edgecosts(this_,y,z,delta);
|
|
|
|
/* delete node x */
|
|
remove_node(this_,x);
|
|
|
|
/* simplify cost matrix c_yz */
|
|
simplify_edge(this_,y,z);
|
|
|
|
/* reorder adj. nodes */
|
|
reorder_adjnodes(this_,x);
|
|
|
|
/* push node x on stack */
|
|
assert(this_ -> stack_ptr < this_ -> num_nodes);
|
|
this_->stack[this_ -> stack_ptr++] = x;
|
|
|
|
/* free vec/mat */
|
|
delete c_yx;
|
|
delete c_zx;
|
|
delete delta;
|
|
|
|
/* increment counter for number statistic */
|
|
this_->num_rii++;
|
|
|
|
}
|
|
|
|
/* RN reduction */
|
|
static
|
|
void apply_RN(pbqp *this_,int x)
|
|
{
|
|
unsigned xlen;
|
|
|
|
assert(this_ != NULL);
|
|
assert(x >= 0 && x < this_->num_nodes);
|
|
assert(this_ -> node_costs[x] != NULL);
|
|
|
|
xlen = this_ -> node_costs[x] -> getLength();
|
|
|
|
/* after application of RN rule no optimality
|
|
can be guaranteed! */
|
|
this_ -> optimal = false;
|
|
|
|
/* push node x on stack */
|
|
assert(this_ -> stack_ptr < this_ -> num_nodes);
|
|
this_->stack[this_ -> stack_ptr++] = x;
|
|
|
|
/* delete node x */
|
|
remove_node(this_,x);
|
|
|
|
/* reorder adj. nodes of node x */
|
|
reorder_adjnodes(this_,x);
|
|
|
|
/* increment counter for number statistic */
|
|
this_->num_rn++;
|
|
}
|
|
|
|
|
|
static
|
|
void compute_tc_info(pbqp *this_, adjnode *p)
|
|
{
|
|
adjnode *r;
|
|
PBQPMatrix *m;
|
|
int x,y;
|
|
PBQPVector *c_x, *c_y;
|
|
int *row_inf_counts;
|
|
|
|
assert(p->reverse != NULL);
|
|
|
|
/* set flags */
|
|
r = p->reverse;
|
|
p->tc_valid = true;
|
|
r->tc_valid = true;
|
|
|
|
/* get edge */
|
|
x = r->adj;
|
|
y = p->adj;
|
|
|
|
/* get cost vectors */
|
|
c_x = this_ -> node_costs[x];
|
|
c_y = this_ -> node_costs[y];
|
|
|
|
/* get cost matrix */
|
|
m = pbqp_get_costmatrix(this_, x, y);
|
|
|
|
|
|
/* allocate allowed set for edge (x,y) and (y,x) */
|
|
if (p->tc_safe_regs == NULL) {
|
|
p->tc_safe_regs = (int *) malloc(sizeof(int) * c_x->getLength());
|
|
}
|
|
|
|
if (r->tc_safe_regs == NULL ) {
|
|
r->tc_safe_regs = (int *) malloc(sizeof(int) * c_y->getLength());
|
|
}
|
|
|
|
p->tc_impact = r->tc_impact = 0;
|
|
|
|
row_inf_counts = (int *) alloca(sizeof(int) * c_x->getLength());
|
|
|
|
/* init arrays */
|
|
p->tc_safe_regs[0] = 0;
|
|
row_inf_counts[0] = 0;
|
|
for(unsigned i = 1; i < c_x->getLength(); ++i){
|
|
p->tc_safe_regs[i] = 1;
|
|
row_inf_counts[i] = 0;
|
|
}
|
|
|
|
r->tc_safe_regs[0] = 0;
|
|
for(unsigned j = 1; j < c_y->getLength(); ++j){
|
|
r->tc_safe_regs[j] = 1;
|
|
}
|
|
|
|
for(unsigned j = 0; j < c_y->getLength(); ++j) {
|
|
int col_inf_counts = 0;
|
|
for (unsigned i = 0; i < c_x->getLength(); ++i) {
|
|
if (isInf((*m)[i][j])) {
|
|
++col_inf_counts;
|
|
++row_inf_counts[i];
|
|
|
|
p->tc_safe_regs[i] = 0;
|
|
r->tc_safe_regs[j] = 0;
|
|
}
|
|
}
|
|
if (col_inf_counts > p->tc_impact) {
|
|
p->tc_impact = col_inf_counts;
|
|
}
|
|
}
|
|
|
|
for(unsigned i = 0; i < c_x->getLength(); ++i){
|
|
if (row_inf_counts[i] > r->tc_impact)
|
|
{
|
|
r->tc_impact = row_inf_counts[i];
|
|
}
|
|
}
|
|
|
|
delete m;
|
|
}
|
|
|
|
/*
|
|
* Checks whether node x can be locally coloured.
|
|
*/
|
|
static
|
|
int is_colorable(pbqp *this_,int x)
|
|
{
|
|
adjnode *adj_ptr;
|
|
PBQPVector *c_x;
|
|
int result = 1;
|
|
int *allowed;
|
|
int num_allowed = 0;
|
|
unsigned total_impact = 0;
|
|
|
|
assert(this_ != NULL);
|
|
assert(x >= 0 && x < this_->num_nodes);
|
|
assert(this_ -> node_costs[x] != NULL);
|
|
|
|
c_x = this_ -> node_costs[x];
|
|
|
|
/* allocate allowed set */
|
|
allowed = (int *)malloc(sizeof(int) * c_x->getLength());
|
|
for(unsigned i = 0; i < c_x->getLength(); ++i){
|
|
if (!isInf((*c_x)[i]) && i > 0) {
|
|
allowed[i] = 1;
|
|
++num_allowed;
|
|
} else {
|
|
allowed[i] = 0;
|
|
}
|
|
}
|
|
|
|
/* determine local minimum */
|
|
for(adj_ptr=this_->adj_list[x] ;adj_ptr != NULL; adj_ptr = adj_ptr -> succ) {
|
|
if (!adj_ptr -> tc_valid) {
|
|
compute_tc_info(this_, adj_ptr);
|
|
}
|
|
|
|
total_impact += adj_ptr->tc_impact;
|
|
|
|
if (num_allowed > 0) {
|
|
for (unsigned i = 1; i < c_x->getLength(); ++i){
|
|
if (allowed[i]){
|
|
if (!adj_ptr->tc_safe_regs[i]){
|
|
allowed[i] = 0;
|
|
--num_allowed;
|
|
if (num_allowed == 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( total_impact >= c_x->getLength() - 1 && num_allowed == 0 ) {
|
|
result = 0;
|
|
break;
|
|
}
|
|
}
|
|
free(allowed);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* use briggs heuristic
|
|
note: this_ is not a general heuristic. it only is useful for
|
|
interference graphs.
|
|
*/
|
|
int pop_colorablenode(pbqp *this_)
|
|
{
|
|
int deg;
|
|
bucketnode *min_bucket=NULL;
|
|
PBQPNum min = std::numeric_limits<PBQPNum>::infinity();
|
|
|
|
/* select node where the number of colors is less than the node degree */
|
|
for(deg=this_->max_deg;deg > 2;deg--) {
|
|
bucketnode *bucket;
|
|
for(bucket=this_->bucket_list[deg];bucket!= NULL;bucket = bucket -> succ) {
|
|
int u = bucket->u;
|
|
if (is_colorable(this_,u)) {
|
|
pbqp_remove_bucket(this_,bucket);
|
|
this_->num_rn_special++;
|
|
free(bucket);
|
|
return u;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* select node with minimal ratio between average node costs and degree of node */
|
|
for(deg=this_->max_deg;deg >2; deg--) {
|
|
bucketnode *bucket;
|
|
for(bucket=this_->bucket_list[deg];bucket!= NULL;bucket = bucket -> succ) {
|
|
PBQPNum h;
|
|
int u;
|
|
|
|
u = bucket->u;
|
|
assert(u>=0 && u < this_->num_nodes);
|
|
h = (*this_->node_costs[u])[0] / (PBQPNum) deg;
|
|
if (h < min) {
|
|
min_bucket = bucket;
|
|
min = h;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* return node and free bucket */
|
|
if (min_bucket != NULL) {
|
|
int u;
|
|
|
|
pbqp_remove_bucket(this_,min_bucket);
|
|
u = min_bucket->u;
|
|
free(min_bucket);
|
|
return u;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* PBQP graph parsing
|
|
****************************************************************************/
|
|
|
|
/* reduce pbqp problem (first phase) */
|
|
static
|
|
void reduce_pbqp(pbqp *this_)
|
|
{
|
|
int u;
|
|
|
|
assert(this_ != NULL);
|
|
assert(this_->bucket_list != NULL);
|
|
|
|
for(;;){
|
|
|
|
if (this_->bucket_list[1] != NULL) {
|
|
u = pop_node(this_,1);
|
|
apply_RI(this_,u);
|
|
} else if (this_->bucket_list[2] != NULL) {
|
|
u = pop_node(this_,2);
|
|
apply_RII(this_,u);
|
|
} else if ((u = pop_colorablenode(this_)) != -1) {
|
|
apply_RN(this_,u);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PBQP back propagation
|
|
****************************************************************************/
|
|
|
|
/* determine solution of a reduced node. Either
|
|
RI or RII was applied for this_ node. */
|
|
static
|
|
void determine_solution(pbqp *this_,int x)
|
|
{
|
|
PBQPVector *v = new PBQPVector(*this_ -> node_costs[x]);
|
|
adjnode *adj_ptr;
|
|
|
|
assert(this_ != NULL);
|
|
assert(x >= 0 && x < this_->num_nodes);
|
|
assert(this_ -> adj_list != NULL);
|
|
assert(this_ -> solution != NULL);
|
|
|
|
for(adj_ptr=this_->adj_list[x] ;adj_ptr != NULL; adj_ptr = adj_ptr -> succ) {
|
|
int y = adj_ptr -> adj;
|
|
int y_sol = this_ -> solution[y];
|
|
|
|
PBQPMatrix *c_yx = pbqp_get_costmatrix(this_,y,x);
|
|
assert(y_sol >= 0 && y_sol < (int)this_->node_costs[y]->getLength());
|
|
(*v) += c_yx->getRowAsVector(y_sol);
|
|
delete c_yx;
|
|
}
|
|
this_ -> solution[x] = v->minIndex();
|
|
|
|
delete v;
|
|
}
|
|
|
|
/* back popagation phase of PBQP */
|
|
static
|
|
void back_propagate(pbqp *this_)
|
|
{
|
|
int i;
|
|
|
|
assert(this_ != NULL);
|
|
assert(this_->stack != NULL);
|
|
assert(this_->stack_ptr < this_->num_nodes);
|
|
|
|
for(i=this_ -> stack_ptr-1;i>=0;i--) {
|
|
int x = this_ -> stack[i];
|
|
assert( x >= 0 && x < this_ -> num_nodes);
|
|
reinsert_node(this_,x);
|
|
determine_solution(this_,x);
|
|
}
|
|
}
|
|
|
|
/* solve trivial nodes of degree zero */
|
|
static
|
|
void determine_trivialsolution(pbqp *this_)
|
|
{
|
|
int u;
|
|
PBQPNum delta;
|
|
|
|
assert( this_ != NULL);
|
|
assert( this_ -> bucket_list != NULL);
|
|
|
|
/* determine trivial solution */
|
|
while (this_->bucket_list[0] != NULL) {
|
|
u = pop_node(this_,0);
|
|
|
|
assert( u >= 0 && u < this_ -> num_nodes);
|
|
|
|
this_->solution[u] = this_->node_costs[u]->minIndex();
|
|
delta = (*this_->node_costs[u])[this_->solution[u]];
|
|
this_->min = this_->min + delta;
|
|
|
|
/* increment counter for number statistic */
|
|
this_->num_r0++;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* debug facilities
|
|
****************************************************************************/
|
|
static
|
|
void check_pbqp(pbqp *this_)
|
|
{
|
|
int u,v;
|
|
PBQPMatrix *costs;
|
|
adjnode *adj_ptr;
|
|
|
|
assert( this_ != NULL);
|
|
|
|
for(u=0;u< this_->num_nodes; u++) {
|
|
assert (this_ -> node_costs[u] != NULL);
|
|
for(adj_ptr = this_ -> adj_list[u];adj_ptr != NULL; adj_ptr = adj_ptr -> succ) {
|
|
v = adj_ptr -> adj;
|
|
assert( v>= 0 && v < this_->num_nodes);
|
|
if (u < v ) {
|
|
costs = adj_ptr -> costs;
|
|
assert( costs->getRows() == this_->node_costs[u]->getLength() &&
|
|
costs->getCols() == this_->node_costs[v]->getLength());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PBQP solve routines
|
|
****************************************************************************/
|
|
|
|
/* solve PBQP problem */
|
|
void solve_pbqp(pbqp *this_)
|
|
{
|
|
assert(this_ != NULL);
|
|
assert(!this_->solved);
|
|
|
|
/* check vector & matrix dimensions */
|
|
check_pbqp(this_);
|
|
|
|
/* simplify PBQP problem */
|
|
|
|
/* eliminate trivial nodes, i.e.
|
|
nodes with cost vectors of length one. */
|
|
eliminate_trivial_nodes(this_);
|
|
|
|
/* eliminate edges with independent
|
|
cost matrices and normalize matrices */
|
|
eliminate_independent_edges(this_);
|
|
|
|
/* create bucket list for graph parsing */
|
|
create_bucketlist(this_);
|
|
|
|
/* reduce phase */
|
|
reduce_pbqp(this_);
|
|
|
|
/* solve trivial nodes */
|
|
determine_trivialsolution(this_);
|
|
|
|
/* back propagation phase */
|
|
back_propagate(this_);
|
|
|
|
this_->solved = true;
|
|
}
|
|
|
|
/* get solution of a node */
|
|
int get_pbqp_solution(pbqp *this_,int x)
|
|
{
|
|
assert(this_ != NULL);
|
|
assert(this_->solution != NULL);
|
|
assert(this_ -> solved);
|
|
|
|
return this_->solution[x];
|
|
}
|
|
|
|
/* is solution optimal? */
|
|
bool is_pbqp_optimal(pbqp *this_)
|
|
{
|
|
assert(this_ -> solved);
|
|
return this_->optimal;
|
|
}
|
|
|
|
}
|
|
|
|
/* end of pbqp.c */
|