https://hal.archives-ouvertes.fr/hal-03445821
Raw File
exact.c
/*
   Copyright Universite de Versailles Saint-Quentin en Yvelines 2009
   AUTHORS: Sebastien Briais, Sid Touati

   This file is part of RS.
   
   RS is free software: you can redistribute it and/or modify it
   under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation, either version 3 of the
   License, or (at your option) any later version.
   
   RS 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
   Lesser General Public License for more details.
   
   You should have received a copy of the GNU Lesser General Public
   License along with RS.  If not, see
   <http://www.gnu.org/licenses/>.
*/
#include <SCEDA/list.h>
#include <SCEDA/graph.h>
#include <SCEDA/graph_dag.h>
#include <SCEDA/common.h>
#include <SCEDA/graph_antichain.h>

#include "util.h"

#include "exact.h"
#include "pkill.h"
#include "dvk.h"
#include "extk.h"

#include <sys/time.h>

static double ellapsed_time(struct timeval *t0, struct timeval *t1) {
  return ((double)(t1->tv_sec - t0->tv_sec)) + ((double)(t1->tv_usec - t0->tv_usec))/((double)1000000);
}

// reflect the choice "u killed by ku" in G_k
// nodes are given in PK(G), so this is rather "u in PK(G) killed by ku in PK(G)"
static SCEDA_List *update_choice(SCEDA_Graph *gk, SCEDA_HashMap *g_to_gk, SCEDA_Vertex *u_pk, SCEDA_Vertex *ku_pk) {
  SCEDA_List *new_edges = SCEDA_list_create(NULL);

  // translate PK(G) vertices into G vertices
  SCEDA_Vertex *ku = SCEDA_vertex_get_data(SCEDA_Vertex *, ku_pk);
  // get Gk translation
  SCEDA_Vertex *ku_k = SCEDA_hashmap_get(g_to_gk, ku);

  // add edges from potential killers of u to chosen killer of u
  SCEDA_VertexSuccIterator succ;
  SCEDA_vertex_succ_iterator_init(u_pk, &succ);
  while(SCEDA_vertex_succ_iterator_has_next(&succ)) {
    SCEDA_Vertex *v_pk = SCEDA_vertex_succ_iterator_next(&succ);
    if(v_pk != ku_pk) {
      SCEDA_Vertex *v = SCEDA_vertex_get_data(SCEDA_Vertex *, v_pk);
      SCEDA_Vertex *v_k = SCEDA_hashmap_get(g_to_gk, v);
      SCEDA_Edge *e = SCEDA_graph_add_edge(gk, v_k, ku_k, NULL);
      safe_call(SCEDA_list_add(new_edges, e));
    }
  }
  SCEDA_vertex_succ_iterator_cleanup(&succ);

  return new_edges;
}

static void undo_choice(SCEDA_Graph *gk, SCEDA_List *new_edges) {
  SCEDA_ListIterator edges;
  SCEDA_list_iterator_init(new_edges, &edges);
  while(SCEDA_list_iterator_has_next(&edges)) {
    SCEDA_Edge *e = SCEDA_list_iterator_next(&edges);
    void *data = NULL;
    SCEDA_graph_remove_edge(gk, e, &data);
  }
  SCEDA_list_iterator_cleanup(&edges);
  SCEDA_list_clear(new_edges);
}

static SCEDA_Graph *RS_ddag_dvk_opt(GDD_DAG *ddg, const char *type, SCEDA_Graph *pkg, SCEDA_HashMap *killing_map, SCEDA_Graph *g_extk) {
  SCEDA_Graph *dvk_g = SCEDA_graph_create(NULL, NULL);
  SCEDA_Graph *g = GDD_dag_get_ddg(ddg);

  SCEDA_HashMap *g_to_dvkg = SCEDA_vertex_map_create(NULL);

  {
    SCEDA_VerticesIterator g_vertice;
    SCEDA_vertices_iterator_init(g, &g_vertice);
    while(SCEDA_vertices_iterator_has_next(&g_vertice)) {
      SCEDA_Vertex *v = SCEDA_vertices_iterator_next(&g_vertice);
      if(GDD_vertex_is_value_of_type(v, type)) {
	SCEDA_Vertex *dvv = SCEDA_graph_add_vertex(dvk_g, v);
	SCEDA_hashmap_put(g_to_dvkg, v, dvv, NULL);
      } 
    }
    SCEDA_vertices_iterator_cleanup(&g_vertice);
  }

  SCEDA_Graph *g_extk_closure = SCEDA_graph_transitive_closure(g_extk);

  SCEDA_HashMap *g_to_g_extk_closure = SCEDA_vertex_map_create(NULL);
  
  {
    SCEDA_VerticesIterator vertices;
    SCEDA_vertices_iterator_init(g_extk_closure, &vertices);
    while(SCEDA_vertices_iterator_has_next(&vertices)) {
      SCEDA_Vertex *v_extk_closure = SCEDA_vertices_iterator_next(&vertices);
      SCEDA_Vertex *v_extk = SCEDA_vertex_get_data(SCEDA_Vertex *, v_extk_closure);
      SCEDA_Vertex *v = SCEDA_vertex_get_data(SCEDA_Vertex *, v_extk);
      SCEDA_hashmap_put(g_to_g_extk_closure, v, v_extk_closure, NULL);
    }
    SCEDA_vertices_iterator_cleanup(&vertices);
  }

  {
    SCEDA_HashMapIterator killers;
    SCEDA_hashmap_iterator_init(killing_map, &killers);
    while(SCEDA_hashmap_iterator_has_next(&killers)) {
      SCEDA_Vertex *u;
      SCEDA_Vertex *ku = SCEDA_hashmap_iterator_next(&killers, &u);
      SCEDA_Vertex *dvu = SCEDA_hashmap_get(g_to_dvkg, u);
      
      SCEDA_Vertex *ku_extk_closure = SCEDA_hashmap_get(g_to_g_extk_closure, ku);
      SCEDA_VertexSuccIterator desc;
      SCEDA_vertex_succ_iterator_init(ku_extk_closure, &desc);
      while(SCEDA_vertex_succ_iterator_has_next(&desc)) {
	SCEDA_Vertex *v_extk_closure = SCEDA_vertex_succ_iterator_next(&desc);
	SCEDA_Vertex *v_extk = SCEDA_vertex_get_data(SCEDA_Vertex *, v_extk_closure);
	SCEDA_Vertex *v = SCEDA_vertex_get_data(SCEDA_Vertex *, v_extk);
	if(GDD_vertex_is_value_of_type(v, type)) {
	  SCEDA_Vertex *dvv = SCEDA_hashmap_get(g_to_dvkg, v);
	  SCEDA_graph_add_edge(dvk_g, dvu, dvv, NULL);
	}
      }
      SCEDA_vertex_succ_iterator_cleanup(&desc);

      if(GDD_vertex_is_value_of_type(ku, type)) {
	SCEDA_Vertex *dvku = SCEDA_hashmap_get(g_to_dvkg, ku);
	SCEDA_graph_add_edge(dvk_g, dvu, dvku, NULL);
      }
    }
    SCEDA_hashmap_iterator_cleanup(&killers);
  }

  SCEDA_hashmap_delete(g_to_g_extk_closure);
  SCEDA_graph_delete(g_extk_closure);

  SCEDA_hashmap_delete(g_to_dvkg);

  return dvk_g;
}

static SCEDA_List *RS_ddag_saturating_values_opt(GDD_DAG *ddg, const char *type, SCEDA_Graph *pkg, SCEDA_HashMap *killing_map, SCEDA_Graph *g_extk) {
  SCEDA_Graph *dvk = RS_ddag_dvk_opt(ddg, type, pkg, killing_map, g_extk);
  SCEDA_List *amk = RS_ddag_amk(ddg, dvk);
  SCEDA_graph_delete(dvk);
  return amk;
}

static int RS_ddag_register_need_opt(GDD_DAG *ddg, const char *type, SCEDA_Graph *pkg, SCEDA_HashMap *killing_map, SCEDA_Graph *g_extk) {
  SCEDA_Graph *dvk = RS_ddag_dvk_opt(ddg, type, pkg, killing_map, g_extk);
  int n = SCEDA_graph_width(dvk);
  SCEDA_graph_delete(dvk);
  return n;
}

/** Compute the (exact) register saturation for considered type.

    @param[in] ddg = GDD_DAG
    @param[in] type = considered type
    @param[in] timeout = time out in seconds (ignored when < 0)
    @param[out] rs = register saturation
    @param[out] k = killing function (not NULL in case of success
    except when there are no values of given type and thus rs = 0)

    @return TRUE if computed saturation is optimal, FALSE otherwise.
*/
int RS_ddag_compute_saturation(GDD_DAG *ddg, const char *type, double timeout, int *rs_value, SCEDA_HashMap **rs_killing) {
  int abort_timeout = FALSE;
  *rs_value = 0;
  *rs_killing = NULL;

  SCEDA_Graph *pkg = RS_ddag_pkill(ddg, type);

  SCEDA_Vertex *pk_values[SCEDA_graph_vcount(pkg)];

  int n = 0;
  {
    SCEDA_VerticesIterator vertice;
    SCEDA_vertices_iterator_init(pkg, &vertice);
    while(SCEDA_vertices_iterator_has_next(&vertice)) {
      SCEDA_Vertex *pk_v = SCEDA_vertices_iterator_next(&vertice);
      SCEDA_Vertex *v = SCEDA_vertex_get_data(SCEDA_Vertex *, pk_v);
      if(GDD_vertex_is_value_of_type(v, type)) {
	pk_values[n++] = pk_v;
      }
    }
    SCEDA_vertices_iterator_cleanup(&vertice);
  }

  if(n == 0) {
    SCEDA_graph_delete(pkg);
    return TRUE;
  }

#if(1)
  {
    int i, j, min;
    for(i = 0; i < n; i++) {
      min = i;
      SCEDA_Vertex *vmin = pk_values[i];
      for(j = i+1; j < n; j++) {
	SCEDA_Vertex *v = pk_values[j];
	if(SCEDA_vertex_out_deg(v) >= SCEDA_vertex_out_deg(vmin)) {
	  min = j;
	  vmin = v;
	}
      }
      if(min != i) {
	pk_values[min] = pk_values[i];
	pk_values[i] = vmin;
      }
    }
  }
#endif

  SCEDA_Graph *gk = SCEDA_graph_create(NULL, NULL);
  SCEDA_HashMap *g_to_gk = SCEDA_vertex_map_create(NULL);
  {
    SCEDA_Graph *g = GDD_dag_get_ddg(ddg);
    // first copy G
    { 
      // copy vertices
      SCEDA_VerticesIterator vertices;
      SCEDA_vertices_iterator_init(g, &vertices);
      while(SCEDA_vertices_iterator_has_next(&vertices)) {
	SCEDA_Vertex *v = SCEDA_vertices_iterator_next(&vertices);
	SCEDA_Vertex *vk = SCEDA_graph_add_vertex(gk, v);
	SCEDA_hashmap_put(g_to_gk, v, vk, NULL);
      }
      SCEDA_vertices_iterator_cleanup(&vertices);
    }
    { 
      // copy succ relation
      SCEDA_VerticesIterator vertices;
      SCEDA_vertices_iterator_init(g, &vertices);
      while(SCEDA_vertices_iterator_has_next(&vertices)) {
	SCEDA_Vertex *v = SCEDA_vertices_iterator_next(&vertices);
	SCEDA_Vertex *vk = SCEDA_hashmap_get(g_to_gk, v);
	SCEDA_VertexSuccIterator succ;
	SCEDA_vertex_succ_iterator_init(v, &succ);
	while(SCEDA_vertex_succ_iterator_has_next(&succ)) {
	  SCEDA_Vertex *w = SCEDA_vertex_succ_iterator_next(&succ);
	  SCEDA_Vertex *wk = SCEDA_hashmap_get(g_to_gk, w);
	  SCEDA_graph_add_edge(gk, vk, wk, NULL);
	}
	SCEDA_vertex_succ_iterator_cleanup(&succ);
      }
      SCEDA_vertices_iterator_cleanup(&vertices);
    }
  }

  // exhaustive search
  SCEDA_HashMap *current_killing = SCEDA_vertex_map_create(NULL);

  {
    SCEDA_VertexSuccIterator succ[n];
    SCEDA_List *edges[n];

    int i;
    for(i = 0; i < n; i++) {
      edges[i] = NULL;
    }

    struct timeval chrono_start; 
    gettimeofday(&chrono_start, NULL); 

    int backtrack = FALSE;
    for(i = 0; i >= 0;) {
      struct timeval chrono_current; 
      gettimeofday(&chrono_current, NULL); 
      if((timeout > 0) && (ellapsed_time(&chrono_start, &chrono_current) >= timeout)) { 
	abort_timeout = TRUE;
	break;
      }


      SCEDA_Vertex *u_pk = pk_values[i];
      SCEDA_Vertex *u = SCEDA_vertex_get_data(SCEDA_Vertex *, u_pk);
      //
      if(SCEDA_vertex_out_deg(u_pk) == 0) {
	if(backtrack) {
	  i--;
	} else {
	  i++;
	}
      } else {
	if(edges[i] != NULL) {
	  SCEDA_Vertex *ku;
	  safe_call(SCEDA_hashmap_remove(current_killing, (void **)&u, (void **)&ku));

	  SCEDA_List *ku_edges = edges[i];
	  undo_choice(gk, ku_edges);
	  SCEDA_list_delete(ku_edges);

	  edges[i] = NULL;
	} else {
	  SCEDA_vertex_succ_iterator_init(u_pk, &(succ[i]));
	}

	int found = FALSE;
	while(SCEDA_vertex_succ_iterator_has_next(&(succ[i]))) {
	  SCEDA_Vertex *ku_pk = SCEDA_vertex_succ_iterator_next(&(succ[i]));
	  SCEDA_List *ku_edges = update_choice(gk, g_to_gk, u_pk, ku_pk);
	  if(SCEDA_graph_is_acyclic(gk)) {
	    edges[i] = ku_edges;
	    found = TRUE;
	    SCEDA_Vertex *ku = SCEDA_vertex_get_data(SCEDA_Vertex *, ku_pk);
	    SCEDA_hashmap_put(current_killing, u, ku, NULL);
	    break;
	  } else {
	    undo_choice(gk, ku_edges);
	    SCEDA_list_delete(ku_edges);
	  }
	}
	if(found) {
	  backtrack = FALSE;
	  i++;
	} else {
	  SCEDA_vertex_succ_iterator_cleanup(&(succ[i]));
	  backtrack = TRUE;
	  i--;
	}
      }
      if(i == n) {
	int rn = RS_ddag_register_need_opt(ddg, type, pkg, current_killing, gk);
 	if(rn > *rs_value) {
	  *rs_value = rn;
	  if(*rs_killing != NULL) {
	    SCEDA_hashmap_clear(*rs_killing);
	  } else {
	    *rs_killing = SCEDA_vertex_map_create(NULL);
	  }

	  {
	    SCEDA_HashMapIterator kmap;
	    SCEDA_hashmap_iterator_init(current_killing, &kmap);
	    while(SCEDA_hashmap_iterator_has_next(&kmap)) {
	      SCEDA_Vertex *v;
	      SCEDA_Vertex *kv = SCEDA_hashmap_iterator_next(&kmap, &v);
	      SCEDA_hashmap_put(*rs_killing, v, kv, NULL);
	    }
	    SCEDA_hashmap_iterator_cleanup(&kmap);
	  }
 	} 
	
	backtrack = TRUE;
	i--;
      }
    }

    for(i = 0; i < n; i++) {
      if(edges[i] != NULL) {
	SCEDA_List *ku_edges = edges[i];
	undo_choice(gk, ku_edges);
	SCEDA_list_delete(ku_edges);
	edges[i] = NULL;
	SCEDA_vertex_succ_iterator_cleanup(&(succ[i]));
      }
    }
  }


  SCEDA_hashmap_delete(current_killing);
  // finished
  SCEDA_hashmap_delete(g_to_gk);
  SCEDA_graph_delete(gk);

  SCEDA_graph_delete(pkg); 

  return !abort_timeout;
}

back to top