| //===---------------- 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 */ |