/* Copyright 2020 Christophe Crespelle */ /* This file is part of Coedit. */ /* Coedit 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 3 of the License, or (at your option) any later version. */ /* Coedit 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 Coedit. If not, see . */ /********************************************************************************** * Name : Coedit * Authors: Christophe Crespelle * Mail : Christophe.Crespelle@inria.fr * Purpose: Minimal cograph edition, completion and deletion of an arbitrary graph *********************************************************************************/ #include #include #include #include #include #include #include #include "semigraph_prelim.c" #include "cotree_prelim.c" /******************/ /* Default values */ /******************/ /********************/ /* Output functions */ /********************/ // print help void usage(char *c){ fprintf(stderr,"Usage: %s -h\n",c); fprintf(stderr,"Usage: %s -a\n",c); fprintf(stderr,"Usage: %s -d\n",c); fprintf(stderr,"Usage: %s -e\n",c); // fprintf(stderr,"Usage: %s -r number\n",c); fprintf(stderr,"Usage: %s -r\n",c); fprintf(stderr,"Usage: %s -o basename\n",c); fprintf(stderr," -h: print current help.\n"); fprintf(stderr," -a: compute a minimal cograph completion of the input graph.\n"); fprintf(stderr," -d: compute a minimal cograph deletion of the input graph.\n"); fprintf(stderr," -e: compute a minimal cograph edition of the input graph.\n"); // fprintf(stderr," -r: consider the vertices in a random order, use \"number\" different orders (and choose the best result).\n"); fprintf(stderr," -r: consider the vertices in a random order.\n"); fprintf(stderr," -ord filename: consider the vertices in the order given in the file named \"filename\".\n"); fprintf(stderr," -pm: output modifications of the original graph (default is no).\n"); fprintf(stderr," -v: verbose mode, includes a global report.\n"); fprintf(stderr," -V: super verbose mode, also includes a short report for each vertex.\n"); fprintf(stderr," -i filename: use file named \"filename\" as input (stdin by default).\n"); fprintf(stderr," -o basename: output the results (edits and cotree) in two files named based on \"basename\" (both in stdout by default).\n"); fprintf(stderr," -j basename: write report in a file named based on \"basename\" (stderr by default).\n"); exit(-1); } /***************************/ /* Other utility functions */ /***************************/ int choose_node(int * list, int size) { if ((size==0) || (list==NULL)) report_error("choose_node: empty list."); return list[rand_imp(size)]; //debug : derandomize //return list[0]; } /**************************************************************/ /**************************************************************/ /**************************************************************/ /*************************** ***************************/ /*************************** MAIN ***************************/ /*************************** ***************************/ /**************************************************************/ /**************************************************************/ /**************************************************************/ int main (int argc, char **argv) { /////////////////////////////////////////////////////////// ////////// DECLARATIONS AND DEFAULT VALUES //////////// /////////////////////////////////////////////////////////// // DEBUGGING OPTIONS int debug = 0; // 0: no debug; 1: debugging mode activated int consistency = 0; // 0: no consistency check; 1: consistency check activated int iter_debut_check=2000000000; //int iter_debut_check=1301849; //int iter_debut_check=1205702; //int iter_debut_check=1161377; //int iter_debut_check=1147295; //int iter_debut_check=1147255; sgraph *G=NULL; // input semigraph dyncotree *dynC=NULL; supinfo_edit *Info=NULL; int * permu_new=NULL; int * list_insert_nodes=NULL; int * list_nb_filled=NULL; int * list_nb_emptied=NULL; int nb_insert_nodes; unsigned long nb_min_editions; int insert_node; int nb_child_tobefilled; int min_modif_cost; int nb_add_edges; int nb_del_edges; //int nb_add_compare; //int nb_del_compare; int x; int is_complemented; int old_0=0; // beginning of the children at children[old_0], old_0=0 before renumbering if the user asks //time_t tstart=time(NULL); // time the programm started //int *nb_edges=NULL; int i; //int k; //int l; long int tot_nb_modifs=0; long int tot_add_edges=0; long int tot_del_edges=0; //int * permu1=NULL; //int * pos1=NULL; /////////////////////////////////////////////////////////// //////////////// PARSE COMMAND LINE /////////////////// /////////////////////////////////////////////////////////// // default values FILE* forder=NULL; FILE* fin=stdin; FILE* fout_modifs=stdout; FILE* fout_cotree=stdout; FILE* freport=stderr; FILE* fpermu=stderr; // FILE *fin_cot=NULL; int input_file_given=0; int output_file_given=0; // 0=NO; 1=YES; int journal_file_given=0; char* fname_order=""; char* fname_in=""; char* bname_out=""; // basename of the output file (if requested) char* bname_journal=""; int modif_method=-1; // modif_method: 0=delete; 1=add; 2=edit int order_method=0; // order_method: 0=no reordering; 1=purely random; 2= given order file // int nb_repet=1; // nb_repet: number of times the modification algorithm is run with a different order of the vertices int output_modifs=0; int verbose=0; int ask_modif=0; // ask_method: a method of modification has already been asked in the list of options int ask_order=0; // ask_order: a method for ordering the vertices has already been asked in the list of options // user's values for (i=1; i=1) fprintf(stderr," vertices order read from file %s\n",fname_in); } if (input_file_given==1) { fin=NULL; if ( (fin=fopen(fname_in,"r"))==NULL) report_error("fname_in -- fopen: error"); if (verbose>=1) fprintf(stderr," input read from file %s\n",fname_in); } if (output_file_given==1) { char* fname_modifs=NULL; char* fname_cotree=NULL; if( (fname_modifs=(char *)malloc((strlen(bname_out)+20)*sizeof(char))) == NULL ) report_error("main: malloc() error"); strcpy(fname_modifs,bname_out); if( (fname_cotree=(char *)malloc((strlen(bname_out)+20)*sizeof(char))) == NULL ) report_error("main: malloc() error"); strcpy(fname_cotree,bname_out); if (modif_method==0) {strcat(fname_modifs,"_dele"); strcat(fname_cotree,"_dele");} else if (modif_method==1) {strcat(fname_modifs,"_comp"); strcat(fname_cotree,"_comp");} else if (modif_method==2) {strcat(fname_modifs,"_edit"); strcat(fname_cotree,"_edit");} strcat(fname_modifs,"_modifs"); strcat(fname_cotree,"_cotree"); if (output_modifs==1) { fout_modifs=NULL; if ( (fout_modifs=fopen(fname_modifs,"w"))==NULL) report_error("fname_modifs -- fopen: error"); if (verbose>=1) fprintf(stderr," outputs list of modifications in file %s\n",fname_modifs); free(fname_modifs); } fout_cotree=NULL; if ( (fout_cotree=fopen(fname_cotree,"w"))==NULL) report_error("fname_cotree -- fopen: error"); if (verbose>=1) fprintf(stderr," outputs cotree in file %s\n",fname_cotree); if (fname_modifs!=NULL) free(fname_cotree); } if (journal_file_given==1) { freport=NULL; fpermu=NULL; char* fname_journal=NULL; char* fname_permu=NULL; if( (fname_journal=(char *)malloc((strlen(bname_journal)+20)*sizeof(char))) == NULL ) report_error("main: malloc() error"); if( (fname_permu=(char *)malloc((strlen(bname_journal)+20)*sizeof(char))) == NULL ) report_error("main: malloc() error"); strcpy(fname_journal,bname_journal); if (modif_method==0) strcat(fname_journal,"_dele"); else if (modif_method==1) strcat(fname_journal,"_comp"); else if (modif_method==2) strcat(fname_journal,"_edit"); strcpy(fname_permu,fname_journal); strcat(fname_journal,"_report"); strcat(fname_permu,"_permut"); if ( (freport=fopen(fname_journal,"w"))==NULL) report_error("fname_journal -- fopen: error"); if (verbose>=1) fprintf(stderr," report written in file %s\n",fname_journal); if ( (fpermu=fopen(fname_permu,"w"))==NULL) report_error("fname_permu -- fopen: error"); if (verbose>=1) fprintf(stderr," permutation of vertices written in file %s\n",fname_permu); free(fname_journal); free(fname_permu); } //debug FILE* fbug=NULL; FILE* fdync=NULL; FILE* fcot_restart=NULL; FILE* fsupinfo=NULL; char sup_name[100]; char bug_name[100]; char dync_name[100]; char cot_restart_name[100]; // debug if (debug==1) { strcpy(bug_name,bname_journal); //strcat(bug_name,dicho); strcat(bug_name,"_debug_log"); if ( (fbug=fopen(bug_name,"w"))==NULL ) report_error("debug_log -- fopen: error"); } else if (consistency==1) { strcpy(bug_name,bname_journal); //strcat(bug_name,dicho); strcat(bug_name,"_debug_log"); if ( (fbug=fopen(bug_name,"w"))==NULL) report_error("debug_log -- fopen: error"); } if (consistency==1) { strcpy(dync_name,bname_journal); //strcat(dync_name,dicho); strcat(dync_name,"_dync_consist"); if ( (fdync=fopen(dync_name,"w"))==NULL) report_error("dync_consist -- fopen: error"); strcpy(cot_restart_name,bname_journal); //strcat(cot_restart_name,dicho); strcat(cot_restart_name,"_cot_restart"); if ( (fcot_restart=fopen(cot_restart_name,"w"))==NULL) report_error("cot_restart -- fopen: error"); } if (debug==1) { strcpy(sup_name,bname_journal); strcat(sup_name,"_supinfo"); if ( (fsupinfo=fopen(sup_name,"w"))==NULL ) report_error("supinfo -- fopen: error"); } /////////////////////////////////////////////////////////// ///////////////// LOAD THE GRAPH ////////////////////// /////////////////////////////////////////////////////////// if (verbose>=1) fprintf(stderr,"Reading the graph...\n"); G = sgraph_from_file(fin); fprintf(stderr,"%d vertices, %d edges in the graph.\n",G->n,G->m); fflush(stderr); if (input_file_given==1) { fclose(fin); } init_permu(G); /////////////////////////////////////////////////////////// ////////// INITIALISE THE DATA STRUCTURE ////////////// /////////////////////////////////////////////////////////// // for all random choices in the algorithm: 1) ordering of vertices and 2) choice of insertion node at each incremental step init_random(); // reorder the vertices of the graph, if asked if (order_method==1) { if (verbose>=1) fprintf(freport,"Randomly reordering vertices...\n"); // generate a new random permu and assign corresponding g->pos values permu_new=random_perm(G->permu,G->n); reorder_sgraph(G,permu_new); if (verbose>=1) fprintf(freport," ... reordered!\n\n"); fflush(freport); if (permu_new!=NULL) free(permu_new); } else if (order_method==2) { if (verbose>=1) fprintf(freport,"Reordering vertices according to specified vertex file ...\n"); if( (permu_new=(int *)malloc(G->n*sizeof(int))) == NULL ) report_error("main: malloc() error with permu_new"); read_order_from_file(forder,G->n,permu_new); reorder_sgraph(G,permu_new); if (verbose>=1) fprintf(freport," ... reordered!\n\n"); fflush(freport); fclose(forder); if (permu_new!=NULL) free(permu_new); } // output the order used for the vertices fprintf(freport,"Order used for the vertices:\n"); if (journal_file_given==1) { fprintf(freport," -> see file *_permut.\n"); for (i=0;in;i++) fprintf(fpermu,"%d\n",G->permu[i]); fflush(fpermu); } else { for (i=0;in;i++) fprintf(freport,"%d\n",G->permu[i]); fflush(freport); } if (journal_file_given==1) { fclose(fpermu); } ////////////////// INIT DYNCOTREE ////////////////// if (verbose>=1) fprintf(freport,"\nInitialise dyncotree with 2 leaves...\n"); dynC=init_dyncotree_2leaves(G); if (verbose>=1) fprintf(freport," ... initialised!\n"); fflush(freport); ////////////////////////////////////////////////////// if (verbose>=1) fprintf(freport,"\nInitialise supinfo...\n"); Info=init_supinfo_edit(dynC); if (verbose>=1) fprintf(freport," ... initialised!\n"); fflush(freport); // IMPORTANT NOTE: x is the position of the incoming vertex in the semigraph G, new_x is the position of the incoming vertex in the dynamic cotree, which is shifted by +dync->n /////////////////////////////////////////////////////////// ///////////// INCREMENTAL ALGORITHM /////////////////// /////////////////////////////////////////////////////////// for (x=2; xn;x++) { is_complemented=0; if (verbose>=2) fprintf(freport,"\nVertex %d incoming.\n",x); fflush(freport); /////////////////////////////////////////////////////////// /////// ASSIGN ADDITIONAL INFO ON THE TREE //////////// assign_supinfo_edit(fdync,fsupinfo,consistency,debug,fbug,dynC, G, x, iter_debut_check, Info, modif_method); /////////////////////////////////////////////////////////// //////////// FIND ALL INSERTION NODES ///////////////// if( (list_insert_nodes=(int *)malloc(dynC->n*sizeof(int))) == NULL ) // size dynC->n assume that leaves cannot be insertion nodes -> to be checked report_error("main: malloc() error with list_insert_nodes"); if ((modif_method==0) || (modif_method==1)) { if ((modif_method==0) && (Info->nb_child_nh[dynC->root]!=0)) { complement_cot_info(dynC,Info); is_complemented=1; } list_minimum_insertion_nodes (dynC, Info, list_insert_nodes, &nb_insert_nodes, &min_modif_cost); } if (modif_method==2) { if( (list_nb_filled=(int *)malloc(dynC->n*sizeof(int))) == NULL ) // size dynC->n assume that leaves cannot be insertion nodes -> to be checked report_error("main: malloc() error with list_insert_nodes"); if( (list_nb_emptied=(int *)malloc(dynC->n*sizeof(int))) == NULL ) // size dynC->n assume that leaves cannot be insertion nodes -> to be checked report_error("main: malloc() error with list_insert_nodes"); list_minimum_editions (debug,x,fbug, dynC, Info, list_insert_nodes, list_nb_filled, list_nb_emptied, &nb_insert_nodes, &nb_min_editions, &min_modif_cost); } // pour edit compter séparément les ajouts et les retraits tot_nb_modifs += (long int) min_modif_cost; if (modif_method==2) if (verbose>=2) fprintf(freport,"Nb of min editions: %lu\n", nb_min_editions); if (verbose>=2) fprintf(freport,"Nb of min insertion nodes: %d\n", nb_insert_nodes); if (verbose>=2) fprintf(freport,"Minimum modification cost: %d\n", min_modif_cost); fflush(freport); /////////////////////////////////////////////////////////// //////////// CHOOSE AN INSERTION NODE ///////////////// // CHOOSE AN INSERTION NODE AND A MINIMUM EDITION SETTLED AT IT if ((modif_method==0) || (modif_method==1)) { insert_node=choose_node(list_insert_nodes, nb_insert_nodes); nb_child_tobefilled=Info->nb_child_nh[insert_node]; if (output_modifs==1) {print_added_edges(fout_modifs,dynC,insert_node,Info,G,x); } if (is_complemented) { complement_cot_info(dynC,Info); nb_child_tobefilled = dynC->nb_child[insert_node]-nb_child_tobefilled; } } if (modif_method==2) { choose_min_edition(dynC, list_insert_nodes, list_nb_filled, list_nb_emptied, nb_insert_nodes, nb_min_editions, &insert_node, &nb_child_tobefilled); if (output_modifs==1) {print_edits(fout_modifs, dynC, insert_node, nb_child_tobefilled, Info, G,x,&nb_add_edges,&nb_del_edges,output_modifs);} else {compute_del_add(dynC, Info, insert_node, nb_child_tobefilled, &nb_add_edges, &nb_del_edges);} tot_add_edges += (long int) nb_add_edges; tot_del_edges += (long int) nb_del_edges; if (verbose>=2) fprintf(freport,"Nb of added edges: %d\n", nb_add_edges); if (verbose>=2) fprintf(freport,"Nb of deleted edges: %d\n", nb_del_edges); fflush(freport); } clear_supinfo_edit(debug,fbug,x,Info,dynC); /////////////////////////////////////////////////////////// /////// UPDATE THE COTREE UNDER INSERTION OF X //////// // UPDATE THE COTREE UNDER INSERTION OF X update_under_insert(debug,fbug,x,dynC,insert_node,nb_child_tobefilled); if (list_insert_nodes!=NULL) free(list_insert_nodes); list_insert_nodes=NULL; if (list_nb_filled!=NULL) free(list_nb_filled); list_nb_filled=NULL; if (list_nb_emptied!=NULL) free(list_nb_emptied); list_nb_emptied=NULL; } /////////////////////////////////////////////////////////// ///////////////// OUTPUT RESULTS ////////////////////// /////////////////////////////////////////////////////////// write_dyncotree_to_file(dynC, G, fout_cotree); if (verbose>=1) fprintf(freport,"\nLost space:\n%ld\n",lost_space); if (verbose>=1) fprintf(freport,"Nb tab truncated:\n%ld\n",tab_truncated); if (verbose>=1) fprintf(freport,"\nTime spent copying:\n%ld\n",copy_time); if (verbose>=1) fprintf(freport,"Nb tab expansion:\n%ld\n",tab_expansion); fprintf(freport,"\nTotal number of modifications:\n%ld\n",tot_nb_modifs); if (modif_method==2) { fprintf(freport,"Total number of added edges:\n%ld\n",tot_add_edges); fprintf(freport,"Total number of deleted edges:\n%ld\n",tot_del_edges); } fflush(freport); /////////////////////////////////////////////////////////// //////////////////// FREE MEMORY ////////////////////// /////////////////////////////////////////////////////////// free_sgraph_old_start(G,old_0); free_dyncot(&dynC); free_supinfo_edit(Info); if (output_file_given==1) { fclose(fout_modifs); fclose(fout_cotree); } if (journal_file_given==1) { fclose(freport); } if (fbug!=NULL) fclose(fbug); return 0; } /**************************************************************/ /**************************************************************/ /************************* *************************/ /************************* FIN MAIN *************************/ /************************* *************************/ /**************************************************************/ /**************************************************************/