Example #1
0
int main(int argc, char *argv[]) {
	
	if(argc < 3){
		cerr << "Uso: input_file max_iteraciones" << endl;
		exit(1);
	}
	
	srand(time(NULL));
	string archivo_entrada(argv[1]);
	int max_iteraciones = atoi(argv[2]);

//----------------------- PARSEO DE ENTRADA	
	pair <int, pair<vector<vector<bool> >*, vector<vector<bool> >* > > grafo = parsear_entrada(archivo_entrada);
	int cant_ejes = grafo.first;
	vector<vector<bool> > *adyacencias = grafo.second.first; // matriz de adyacencia
	vector<vector<bool> > *particion = grafo.second.second;	// filas: subconjuntos de la particion. columnas: nodos.
	
	// Variables binarias:
	//		* X_n_j = nodo n pintado con el color j? (son cant_nodos * cant_colores_disp variables)
	//		* W_j	= hay algun nodo pintado con el color j? (son cant_colores_disp variables)
	//			=> TOTAL: (cant_nodos * cant_colores_disp + cant_colores_disp) variables
	//
	// Orden de las variables:
	//		X_0_0, X_0_1, ... , X_0_(cant_col_disp), X_1_0, ... , X_(cant_nodos)_(cant_col_disp), W_0, ... , W(cant_col_disp)

	int cant_nodos = adyacencias->size();
	int cant_subconj_particion = particion->size(); //cant de subconjuntos de la particion
	int cant_colores_disp = particion->size(); // cant colores usados <= cant de subconjuntos de la particion
	
	int n = cant_nodos * cant_colores_disp + cant_colores_disp; // n = cant de variables

//----------------------- CARGA DE LP
	// Genero el problema de cplex.
	int status;
	CPXENVptr env; // Puntero al entorno.
	CPXLPptr lp; // Puntero al LP
	 
	// Creo el entorno.
	env = CPXopenCPLEX(&status);
		
	if (env == NULL) {
		cerr << "Error creando el entorno" << endl;
		exit(1);
	}
		
	// Creo el LP.
	lp = CPXcreateprob(env, &status, "Coloreo Particionado");

		
	if (lp == NULL) {
		cerr << "Error creando el LP" << endl;
		exit(1);
	}
	
	//TUNNING
	//Para que haga Branch & Cut:
	CPXsetintparam(env, CPX_PARAM_MIPSEARCH, CPX_MIPSEARCH_TRADITIONAL);
	//Para que no se adicionen planos de corte: ( => Branch & Bound)
	CPXsetintparam(env,CPX_PARAM_EACHCUTLIM, 0);
	CPXsetintparam(env, CPX_PARAM_FRACCUTS, -1);
	//Para facilitar la comparación evitamos paralelismo:
	CPXsetintparam(env, CPX_PARAM_THREADS, 1);
	//Para desactivar preprocesamiento
	CPXsetintparam(env, CPX_PARAM_PRESLVND, -1);
	CPXsetintparam(env, CPX_PARAM_REPEATPRESOLVE, 0);
	CPXsetintparam(env, CPX_PARAM_RELAXPREIND, 0);
	CPXsetintparam(env, CPX_PARAM_REDUCE, 0);
	CPXsetintparam(env, CPX_PARAM_LANDPCUTS, -1);
	//Otros parámetros
	// Para desactivar la salida poner CPX_OFF. Para activar: CPX_ON.
	status = CPXsetintparam(env, CPX_PARAM_SCRIND, CPX_OFF);
		if (status) {
			cerr << "Problema seteando SCRIND" << endl;
			exit(1);
		}
	//Setea el tiempo limite de ejecucion.
	status = CPXsetdblparam(env, CPX_PARAM_TILIM, 3600);
		if (status) {
			cerr << "Problema seteando el tiempo limite" << endl;
			exit(1);
		}

	double *ub, *lb, *objfun; // Cota superior, cota inferior, coeficiente de la funcion objetivo.
	char *xctype, **colnames; // tipo de la variable (por ahora son siempre continuas), string con el nombre de la variable.
	ub = new double[n]; 
	lb = new double[n];
	objfun = new double[n];
	xctype = new char[n];
	colnames = new char*[n];
	
	// Defino las variables X_n_j
	for (int i = 0; i < n - cant_colores_disp; i++) {
		ub[i] = 1;
		lb[i] = 0;
		objfun[i] = 0; // Estas var no figuran en la funcion objetivo
		xctype[i] = 'C';
		colnames[i] = new char[10];
		sprintf(colnames[i], "X_%d_%d", i / cant_colores_disp, i % cant_colores_disp);
	}

	// Defino las variables W_j
	for (int i = n - cant_colores_disp; i < n; i++) {
		ub[i] = 1;
		lb[i] = 0;
		objfun[i] = 1;
		xctype[i] = 'C';
		colnames[i] = new char[10];
		sprintf(colnames[i], "W_%d", i - (n - cant_colores_disp));
	}
	
	// Agrego las columnas.
	status = CPXnewcols(env, lp, n, objfun, lb, ub, NULL, colnames);
	
	if (status) {
		cerr << "Problema agregando las variables CPXnewcols" << endl;
		exit(1);
	}
	
	// Libero las estructuras.
	for (int i = 0; i < n; i++) {
		delete[] colnames[i];
	}
	
	delete[] ub;
	delete[] lb;
	delete[] objfun;
	delete[] xctype;
	delete[] colnames;

	// Restricciones:
	//	(1) Nodos adyacentes tienen distinto color (cant_ejes * cant_colores_disp restricciones por <=)
	//	(2) Cada nodo tiene a lo sumo un color (cant_nodos restricciones por <=)
	//	(3) Solo un nodo de cada subconj. de la particion tiene color (cant. de subconj. de la particion restricciones por =)
	//	(4) W_j = 1 sii "X_i_j = 1 para algún i" (cant_colores_disp restricciones por >=)
	//	(5) W_j >= W_(j+1) (cant_colores_disp - 1 restricciones por >=)
	//		=> TOTAL: (cant_ejes * cant_colores_disp + cant_nodos + cant_subconj_particion + cant_colores_disp + cant_colores_disp - 1) restricciones

	int ccnt = 0; //numero nuevo de columnas en las restricciones.
	int rcnt = cant_ejes * cant_colores_disp + cant_nodos + cant_subconj_particion + cant_colores_disp + cant_colores_disp - 1; //cuantas restricciones se estan agregando.
	int nzcnt = 0; //# de coeficientes != 0 a ser agregados a la matriz. Solo se pasan los valores que no son cero.

	char sense[rcnt]; // Sentido de la desigualdad. 'G' es mayor o igual y 'E' para igualdad.
	for(unsigned int i = 0; i < cant_ejes * cant_colores_disp; i++)
		sense[i] = 'L';
	for(unsigned int i = cant_ejes * cant_colores_disp; i < cant_ejes * cant_colores_disp + cant_nodos; i++)
		sense[i] = 'L';
	for(unsigned int i = cant_ejes * cant_colores_disp + cant_nodos; i < cant_ejes * cant_colores_disp + cant_nodos + cant_subconj_particion; i++)
		sense[i] = 'E';
	for(unsigned int i = cant_ejes * cant_colores_disp + cant_nodos + cant_subconj_particion; i < rcnt; i++)
		sense[i] = 'G';

	double *rhs = new double[rcnt]; // Termino independiente de las restricciones.
	int *matbeg = new int[rcnt]; //Posicion en la que comienza cada restriccion en matind y matval.
	int *matind = new int[rcnt*n]; // Array con los indices de las variables con coeficientes != 0 en la desigualdad.
	double *matval = new double[rcnt*n]; // Array que en la posicion i tiene coeficiente ( != 0) de la variable cutind[i] en la restriccion.

	//El termino indep. de restr (1), (2) y (3) es 1
	for(unsigned int i = 0; i < cant_ejes * cant_colores_disp + cant_nodos + cant_subconj_particion; i++)
		rhs[i] = 1;
		
	//El termino indep. de restr (4) y (5) es 0
	for(unsigned int i = cant_ejes * cant_colores_disp + cant_nodos + cant_subconj_particion; i < rcnt; i++)
		rhs[i] = 0;
	
	unsigned int indice = 0; //numero de restriccion actual
	
	//Restricciones (1)
	for(unsigned int i = 0; i < cant_nodos; i++) //itero nodo 1
		for(unsigned int j = i+1; j < cant_nodos; j++) //itero nodo 2
			if((*adyacencias)[i][j])
				for(unsigned int p = 0; p < cant_colores_disp; p++){ //itero color
					matbeg[indice] = nzcnt;
					indice++;
					//cargo una de las variables participantes de la restr.
					matind[nzcnt] = cant_colores_disp*i + p; //var1: X_nodo1_color
					matval[nzcnt] = 1;
					nzcnt++;
					//idem con la otra variable
					matind[nzcnt] = cant_colores_disp*j + p; //var2: X_nodo2_color
					matval[nzcnt] = 1;
					nzcnt++;
				}
				
	//Restricciones (2)
	for(unsigned int i = 0; i < cant_nodos; i++){ //itero nodo
		matbeg[indice] = nzcnt;
		indice++;
		for(unsigned int p = 0; p < cant_colores_disp; p++){ //itero color
			matind[nzcnt] = cant_colores_disp*i + p; //var: X_nodo_color
			matval[nzcnt] = 1;
			nzcnt++;
		}
	}
	
	//Restricciones (3)
	for(unsigned int v = 0; v < cant_subconj_particion; v++){ //itero subconjunto de la particion
		matbeg[indice] = nzcnt;
		indice++;
		for(unsigned int i = 0; i < cant_nodos; i++) //itero nodo
			if((*particion)[v][i])
				for(unsigned int p = 0; p < cant_colores_disp; p++){ //itero color
					matind[nzcnt] = cant_colores_disp*i + p; //var: X_nodo_color
					matval[nzcnt] = 1;
					nzcnt++;
				}
	}
	
	//Restricciones (4)
	for(unsigned int p = 0; p < cant_colores_disp; p++){ //itero color
		matbeg[indice] = nzcnt;
		indice++;
		matind[nzcnt] = cant_nodos * cant_colores_disp + p; //var: W_color
		matval[nzcnt] = cant_nodos;
		nzcnt++;
		for(unsigned int i = 0; i < cant_nodos; i++){ //itero nodo
			matind[nzcnt] = cant_colores_disp*i + p; //var: X_nodo_color
			matval[nzcnt] = -1;
			nzcnt++;
		}
	}
	
	//Restricciones (5)
	for(unsigned int p = 0; p < cant_colores_disp - 1; p++){ //itero color
		matbeg[indice] = nzcnt;
		indice++;
		matind[nzcnt] = cant_nodos * cant_colores_disp + p; //var: W_color
		matval[nzcnt] = 1;
		nzcnt++;
		matind[nzcnt] = cant_nodos * cant_colores_disp + p + 1; //var: W_(color+1)
		matval[nzcnt] = -1;
		nzcnt++;
	}
	
	// Esta rutina agrega la restriccion al lp.
	status = CPXaddrows(env, lp, ccnt, rcnt, nzcnt, rhs, sense, matbeg, matind, matval, NULL, NULL);
	
	if (status) {
		cerr << "Problema agregando restricciones." << endl;
		exit(1);
	}
	
	delete[] rhs;
	delete[] matbeg;
	delete[] matind;
	delete[] matval;

	// Escribimos el problema a un archivo .lp
	status = CPXwriteprob(env, lp, "output.lp", NULL);
		
	if (status) {
		cerr << "Problema escribiendo modelo" << endl;
		exit(1);
	}
	
//----------------------- PRIMER ITERACION DE RESOLUCIÓN DEL LP
	
	// Tomamos el tiempo de resolucion utilizando CPXgettime.
	double inittime, endtime, fractpart, intpart, opt_anterior, opt_actual;
	int cant_iteraciones = 0;
	status = CPXgettime(env, &inittime);
	
	bool criterio_de_corte, todas_enteras, hubo_plano = true;
	
	status = CPXlpopt(env, lp);
	if (status) {
		cerr << "Problema optimizando CPLEX" << endl;
		exit(1);
	}
	
	status = CPXgetobjval(env, lp, &opt_actual);
	if (status) {
		cerr << "Problema obteniendo valor de mejor solucion." << endl;
		exit(1);
	}
	
	cout << "Optimo Inicial: " << opt_actual << endl << endl;
	
	double *sol = new double[n];
	status = CPXgetx(env, lp, sol, 0, n - 1);
	if (status) {
		cerr << "Problema obteniendo la solucion del LP." << endl;
		exit(1);
	}

	// Chequeo si la solución es entera
	for (int i = 0; i < n; i++){
		fractpart = modf(sol[i] , &intpart);
		if (fractpart > TOL){
			todas_enteras = false;
			break;
			}
		}
	
	criterio_de_corte = todas_enteras || max_iteraciones==0;

//----------------------- INICIO CICLO DE RESOLUCIÓN DEL LP
	while(!criterio_de_corte){
		opt_anterior = opt_actual;
		
		hubo_plano = agregar_restricciones_clique(adyacencias, sol, env, lp, cant_colores_disp, n);
		hubo_plano = agregar_restricciones_ciclos(adyacencias, sol, env, lp, cant_colores_disp, n) || hubo_plano;
		
		if(hubo_plano){
			status = CPXlpopt(env, lp);
			if (status) {
				cerr << "Problema optimizando CPLEX" << endl;
				exit(1);
			}
			
			status = CPXgetx(env, lp, sol, 0, n - 1);
			if (status) {
				cerr << "Problema obteniendo la solucion del LP." << endl;
				exit(1);
			}
			
			for (int i = 0; i < n; i++){
				fractpart = modf(sol[i] , &intpart);
				if (fractpart > TOL){
					todas_enteras = false;
					break;
				}
			}
		}
		
		status = CPXgetobjval(env, lp, &opt_actual);
		if (status) {
			cerr << "Problema obteniendo valor de mejor solucion." << endl;
			exit(1);
		}
		
		cant_iteraciones++;
		criterio_de_corte = todas_enteras || (cant_iteraciones >= max_iteraciones)
								|| !hubo_plano;// || abs(opt_actual - opt_anterior) < TOL;
	}

	status = CPXgettime(env, &endtime);
//----------------------- FIN CICLO DE RESOLUCIÓN DEL LP

	int solstat;
	char statstring[510];
	CPXCHARptr p;
	solstat = CPXgetstat(env, lp);
	p = CPXgetstatstring(env, solstat, statstring);
	string statstr(statstring);
	cout << endl << "Resultado de la optimizacion: " << statstring << endl;
	
	if(solstat!=CPX_STAT_OPTIMAL) exit(1);
	
	double objval;
	status = CPXgetobjval(env, lp, &objval);
		
	if (status) {
		cerr << "Problema obteniendo valor de mejor solucion." << endl;
		exit(1);
	}
		
	cout << "Optimo: " << objval << "\t(Time: " << (endtime - inittime) << " sec)" << endl; 

	// Tomamos los valores de la solucion y los escribimos a un archivo.
	std::string outputfile = "output.sol";
	ofstream solfile(outputfile.c_str());

	// Tomamos los valores de todas las variables. Estan numeradas de 0 a n-1.
	status = CPXgetx(env, lp, sol, 0, n - 1);

	if (status) {
		cerr << "Problema obteniendo la solucion del LP." << endl;
		exit(1);
	}

	// Solo escribimos las variables distintas de cero (tolerancia, 1E-05).
	solfile << "Status de la solucion: " << statstr << endl;
	// Imprimo var X_n_j
	for (int i = 0; i < n - cant_colores_disp; i++) {
		if (sol[i] > TOL) {
			solfile << "X_" << i / cant_colores_disp << "_" << i % cant_colores_disp << " = " << sol[i] << endl;
		}
	}
	// Imprimo var W_j
	for (int i = n - cant_colores_disp; i < n; i++) {
		if (sol[i] > TOL) {
			solfile << "W_" << i - (n - cant_colores_disp) << " = " << sol[i] << endl;
		}
	}

	solfile.close();
	delete [] sol;
	delete adyacencias;
	delete particion;
	
	return 0;
}
Example #2
0
void MC_readproblem(MC_tm& tm)
{
  int i, j, k;
  MC_problem &mc = tm.mc;

  std::ifstream file(tm.tm_par.entry(MC_tm_par::InputFile).c_str());
  if (! file) {
    throw BCP_fatal_error("Can't open input file!\n");
  }

  char line[1000];

  double rescale = 1;
  for (int lose = tm.tm_par.entry(MC_tm_par::DigitsToLose); lose > 0; --lose) {
     rescale *= 10.0;
  }

  file.getline(line, 999);
  int len = strlen(line);
  if (strncmp(line, "DESC: ggrid", CoinMin(len, 11)) == 0) {
     // Ising problem generated by ggrid. Parse the info.
     mc.ising_problem = true;
     printf("Ising problem detected.\n");
     file.getline(line, 999);
     sscanf(line, "DESC: scaling: %lf", &mc.scaling_factor);
     mc.scaling_factor /= rescale;
  }
  while (strncmp(line, "DESC:", CoinMin(len, 5)) == 0) {
     file.getline(line, 999);
  }

  sscanf(line, "%i%i", &mc.num_nodes, &mc.num_edges);

  const int n = mc.num_nodes;
  const int m = mc.num_edges;
  // this will be definitely enough, even for new edges to connect
  // disconnected components.
  mc.edges = new MC_graph_edge[m + n];

  std::map<intpair, int> edge_map;

  double cost;
  intpair ip;
  for (i = 0, k = 0; i < m; ++i) {
    file.getline(line, 999);
    sscanf(line, "%i%i%lf", &ip.first, &ip.second, &cost);
    if (ip.first < 0 || ip.first >= n || ip.second < 0 || ip.second >= n ||
	(ip.first == ip.second)) {
      char errmsg[200];
      sprintf(errmsg, " bad endnodes %i %i\n", ip.first, ip.second);
      throw BCP_fatal_error(errmsg);
    }
    if (ip.first > ip.second)
      std::swap(ip.first, ip.second);
    const std::map<intpair, int>::const_iterator em = edge_map.find(ip);
    if (em != edge_map.end()) {
      printf("Warning: edge (%i,%i) appears once again.\n",
	     ip.first, ip.second);
      printf("         Collapsing the parallel edges.\n");
      mc.edges[em->second].cost += cost;
    } else {
      edge_map[ip] = k;
      mc.edges[k].tail = ip.first;
      mc.edges[k].head = ip.second;
      mc.edges[k++].cost = cost;
    }
  }
  mc.num_edges = k;

  file.close();

  // throw out the 0 cost edges if it's not an ising problem. For ising
  // problems it's worth to keep the 0 cost edges because then the structure
  // is preserved.
  if (! mc.ising_problem) {
    for (i = 0, k = 0; i < mc.num_edges; ++i) {
      if (mc.edges[i].cost == 0.0) {
	printf("Warning: Discarded edge (%i,%i) as it has final cost 0.\n",
	       mc.edges[i].tail, mc.edges[i].head);
      } else {
	mc.edges[k].tail =  mc.edges[i].tail;
	mc.edges[k].head =  mc.edges[i].head;
	mc.edges[k++].cost =  mc.edges[i].cost;
      }
    }
    mc.num_edges = k;

    // Check connectivity
    int * components = new int[mc.num_nodes];
    const int comp_num =
      mc.num_nodes - MC_components(mc.num_nodes, mc.num_edges,
				   mc.edges, components);
    if (comp_num > 1) {
      printf("There are %i components in the graph. Adding 0 cost edges.\n",
	     comp_num);
      std::set<int> seen;
      seen.insert(components[0]);
      for (i = 0; i < mc.num_nodes; ++i) {
	if (seen.find(components[i]) == seen.end()) {
	  // not seen component. connect it
	  mc.edges[mc.num_edges].tail = 0;
	  mc.edges[mc.num_edges].head = i;
	  mc.edges[mc.num_edges++].cost = 0;
	  seen.insert(components[i]);
	}
      }
    }
    delete[] components;
  }

  // rescale the edges
  for (i = 0; i < mc.num_edges; ++i) {
     const double c = mc.edges[i].cost;
     mc.edges[i].cost = floor(c / rescale + 0.5);
  }
  

  // Negate the cost of the edges (minimizing!) and compute their sum
  mc.sum_edge_weight = 0;
  for (i = 0; i < mc.num_edges; ++i) {
    const double c = - mc.edges[i].cost;
    mc.edges[i].cost = c;
    mc.sum_edge_weight += c;
  }

  mc.create_adj_lists();

  if (mc.ising_problem) {
    const int grid = static_cast<int>(sqrt(mc.num_nodes + 1.0));
    const int num_grid_nodes = grid * grid;
    const int num_grid_edges = 2 * num_grid_nodes;
    const int vertical = num_grid_nodes;
    {
      // enumerate the 4-cycles
      mc.ising_four_cycles = new int[num_grid_nodes * 4];
      int * cycle = mc.ising_four_cycles;
      for (i = 0; i < grid; ++i) {
	for (j = 0; j < grid; ++j) {
	  // (i,j) -> (i,j+1)
	  cycle[0] = i*grid + j;
	  // (i,j+1) -> (i+1,j+1)
	  cycle[1] = vertical + i*grid + ((j+1) % grid);
	  // (i+1,j) -> (i+1,j+1)
	  cycle[2] = ((i+1) % grid) * grid + j;
	  // (i,j) -> (i+1,j)
	  cycle[3] = vertical + i*grid + j;
	  cycle += 4;
	}
      }
    }

    const bool has_extra_node = (mc.num_nodes != num_grid_nodes);
    if (has_extra_node) {
      // enumerate the triangles
      mc.ising_triangles = new int[2 * num_grid_nodes * 3];
      int * triangle = mc.ising_triangles;
      for (i = 0; i < grid; ++i) {
	for (j = 0; j < grid; ++j) {
	  // (i,j) -> (i,j+1) -> extra_node ->
	  triangle[0] = i*grid + j;
	  triangle[1] = num_grid_edges + i*grid + j;
	  triangle[2] = num_grid_edges + i*grid + ((j+1) % grid);
	  if (triangle[1] > triangle[2])
	    std::swap(triangle[1], triangle[2]);
	  triangle += 3;
	  // (i,j) -> (i+1,j) -> extra_node ->
	  triangle[0] = vertical + i*grid + j;
	  triangle[1] = num_grid_edges + i*grid + j;
	  triangle[2] = num_grid_edges + ((i+1) % grid) * grid + j;
	  if (triangle[1] > triangle[2])
	    std::swap(triangle[1], triangle[2]);
	  triangle += 3;
	}
      }
    }

    mc.num_structure_type = 5;

    mc.num_switch_structures = new int[mc.num_structure_type];
    mc.switch_structures = new MC_switch_structure*[mc.num_structure_type];

    // three-node connected structures
    {
      mc.num_switch_structures[0] = num_grid_nodes * 6;
      mc.switch_structures[0] = new MC_switch_structure[num_grid_nodes * 6];
      MC_switch_structure * next_struct = mc.switch_structures[0];

      int nodes[3];

      // enumerate the outgoing edges of three long chains
      for (i = 0; i < grid; ++i) {
	for (j = 0; j < grid; ++j) {
	  //-------------------------------------------------------------------
	  // the three nodes: (i-1,j), (i,j), (i+1,j)
	  //  |
	  //  |
	  nodes[0] = ((i+grid-1)%grid) * grid + j;
	  nodes[1] = i * grid + j;
	  nodes[2] = ((i+1)%grid) * grid + j;
	  MC_fill_structure(mc, *next_struct, 3, nodes);
	  if (next_struct->num_neighbors != 8 + (has_extra_node ? 3 : 0))
	    abort();
	  ++next_struct;

	  //-------------------------------------------------------------------
	  // the three nodes: (i,j-1), (i,j), (i,j+1)
	  //  --
	  nodes[0] = i * grid + ((j+grid-1)%grid);
	  nodes[1] = i * grid + j;
	  nodes[2] = i * grid + ((j+1)%grid);
	  MC_fill_structure(mc, *next_struct, 3, nodes);
	  if (next_struct->num_neighbors != 8 + (has_extra_node ? 3 : 0))
	    abort();
	  ++next_struct;

	  //-------------------------------------------------------------------
	  // the three nodes: (i-1,j), (i,j), (i,j+1)
	  //  |_
	  nodes[0] = ((i+grid-1)%grid) * grid + j;
	  nodes[1] = i * grid + j;
	  nodes[2] = i * grid + ((j+1)%grid);
	  MC_fill_structure(mc, *next_struct, 3, nodes);
	  if (next_struct->num_neighbors != 8 + (has_extra_node ? 3 : 0))
	    abort();
	  ++next_struct;

	  //-------------------------------------------------------------------
	  // (i,j+1), (i,j), (i+1,j)
	  //   _
	  //  |
	  nodes[0] = i * grid + ((j+1)%grid);
	  nodes[1] = i * grid + j;
	  nodes[2] = ((i+1)%grid) * grid + j;
	  MC_fill_structure(mc, *next_struct, 3, nodes);
	  if (next_struct->num_neighbors != 8 + (has_extra_node ? 3 : 0))
	    abort();
	  ++next_struct;

	  //-------------------------------------------------------------------
	  // the three nodes: (i+1,j), (i,j), (i,j-1)
	  //  _
	  //   |
	  nodes[0] = ((i+1)%grid) * grid + j;
	  nodes[1] = i * grid + j;
	  nodes[2] = i * grid + ((j+grid-1)%grid);
	  MC_fill_structure(mc, *next_struct, 3, nodes);
	  if (next_struct->num_neighbors != 8 + (has_extra_node ? 3 : 0))
	    abort();
	  ++next_struct;

	  //-------------------------------------------------------------------
	  // the three nodes: (i,j-1), (i,j), (i-1,j)
	  //  _|
	  nodes[0] = i * grid + ((j+grid-1)%grid);
	  nodes[1] = i * grid + j;
	  nodes[2] = ((i+grid-1)%grid) * grid + j;
	  MC_fill_structure(mc, *next_struct, 3, nodes);
	  if (next_struct->num_neighbors != 8 + (has_extra_node ? 3 : 0))
	    abort();
	  ++next_struct;
	}
      }
    }

    //=========================================================================

    // different four-node connected structures

    // first
    {
      mc.num_switch_structures[1] = 4 * num_grid_nodes;
      mc.switch_structures[1] = new MC_switch_structure[4 * num_grid_nodes];
      MC_switch_structure * next_struct = mc.switch_structures[1];

      int nodes[4];

      // List the outgoing edges from every square
      for (i = 0; i < grid; ++i) {
	for (j = 0; j < grid; ++j) {
	  // _|_
	  nodes[0] = i * grid + j;
	  nodes[1] = i * grid + ((j-1+grid)%grid);
	  nodes[2] = ((i-1+grid)%grid) * grid + j;
	  nodes[3] = i * grid + ((j+1)%grid);
	  MC_fill_structure(mc, *next_struct, 4, nodes);
	  if (next_struct->num_neighbors != 10 + (has_extra_node ? 4 : 0))
	    abort();
	  ++next_struct;

	  // |_
	  // |
	  nodes[0] = i * grid + j;
	  nodes[1] = ((i-1+grid)%grid) * grid + j;
	  nodes[2] = i * grid + ((j+1)%grid);
	  nodes[3] = ((i+1)%grid) * grid + j;
	  MC_fill_structure(mc, *next_struct, 4, nodes);
	  if (next_struct->num_neighbors != 10 + (has_extra_node ? 4 : 0))
	    abort();
	  ++next_struct;

	  // _ _
	  //  |
	  nodes[0] = i * grid + j;
	  nodes[1] = i * grid + ((j+1)%grid);
	  nodes[2] = ((i+1)%grid) * grid + j;
	  nodes[3] = i * grid + ((j-1+grid)%grid);
	  MC_fill_structure(mc, *next_struct, 4, nodes);
	  if (next_struct->num_neighbors != 10 + (has_extra_node ? 4 : 0))
	    abort();
	  ++next_struct;

	  // _|
	  //  |
	  nodes[0] = i * grid + j;
	  nodes[1] = ((i+1)%grid) * grid + j;
	  nodes[2] = i * grid + ((j-1+grid)%grid);
	  nodes[3] = ((i-1+grid)%grid) * grid + j;
	  MC_fill_structure(mc, *next_struct, 4, nodes);
	  if (next_struct->num_neighbors != 10 + (has_extra_node ? 4 : 0))
	    abort();
	  ++next_struct;
	}
      }
    }
    //-------------------------------------------------------------------------
    // second
    {
      mc.num_switch_structures[2] = 4 * num_grid_nodes;
      mc.switch_structures[2] = new MC_switch_structure[4 * num_grid_nodes];
      MC_switch_structure * next_struct = mc.switch_structures[2];

      int nodes[4];

      for (i = 0; i < grid; ++i) {
	for (j = 0; j < grid; ++j) {
	  //   _
	  // _|
	  nodes[0] = i * grid + j;
	  nodes[1] = i * grid + ((j+1)%grid);
	  nodes[2] = ((i-1+grid)%grid) * grid + ((j+1)%grid);
	  nodes[3] = ((i-1+grid)%grid) * grid + ((j+2)%grid);
	  MC_fill_structure(mc, *next_struct, 4, nodes);
	  if (next_struct->num_neighbors != 10 + (has_extra_node ? 4 : 0))
	    abort();
	  ++next_struct;

	  // _
	  //  |_
	  nodes[0] = i * grid + j;
	  nodes[1] = i * grid + ((j+1)%grid);
	  nodes[2] = ((i+1)%grid) * grid + ((j+1)%grid);
	  nodes[3] = ((i+1)%grid) * grid + ((j+2)%grid);
	  MC_fill_structure(mc, *next_struct, 4, nodes);
	  if (next_struct->num_neighbors != 10 + (has_extra_node ? 4 : 0))
	    abort();
	  ++next_struct;

	  // |_
	  //   |
	  nodes[0] = i * grid + j;
	  nodes[1] = ((i+1)%grid) * grid + j;
	  nodes[2] = ((i+1)%grid) * grid + ((j+1)%grid);
	  nodes[3] = ((i+2)%grid) * grid + ((j+1)%grid);
	  MC_fill_structure(mc, *next_struct, 4, nodes);
	  if (next_struct->num_neighbors != 10 + (has_extra_node ? 4 : 0))
	    abort();
	  ++next_struct;

	  //  _|
	  // |
	  nodes[0] = i * grid + j;
	  nodes[1] = ((i+1)%grid) * grid + j;
	  nodes[2] = ((i+1)%grid) * grid + ((j-1+grid)%grid);
	  nodes[3] = ((i+2)%grid) * grid + ((j-1+grid)%grid);
	  MC_fill_structure(mc, *next_struct, 4, nodes);
	  if (next_struct->num_neighbors != 10 + (has_extra_node ? 4 : 0))
	    abort();
	  ++next_struct;
	}
      }
    }
    //-------------------------------------------------------------------------
    // third
    {
      mc.num_switch_structures[3] = num_grid_nodes;
      mc.switch_structures[3] = new MC_switch_structure[num_grid_nodes];
      MC_switch_structure * next_struct = mc.switch_structures[3];

      int nodes[4];

      for (i = 0; i < grid; ++i) {
	for (j = 0; j < grid; ++j) {
	  // the square is (i,j), (i,j+1), (i+1,j+1), (i+1,j)
	  //  _
	  // |_|
	  nodes[0] = i * grid + j;
	  nodes[1] = i * grid + ((j+1)%grid);
	  nodes[2] = ((i+1)%grid) * grid + ((j+1)%grid);
	  nodes[3] = ((i+1)%grid) * grid + j;
	  MC_fill_structure(mc, *next_struct, 4, nodes);
	  if (next_struct->num_neighbors != 8 + (has_extra_node ? 4 : 0))
	    abort();
	  ++next_struct;
	}
      }
    }

    //=========================================================================

    // a 6-node connected structures

    {
      mc.num_switch_structures[4] = 2 * num_grid_nodes;
      mc.switch_structures[4] = new MC_switch_structure[2 * num_grid_nodes];
      MC_switch_structure * next_struct = mc.switch_structures[4];

      int nodes[6];

      for (i = 0; i < grid; ++i) {
	for (j = 0; j < grid; ++j) {
	  //  _ _
	  // |_|_|
	  nodes[0] = i * grid + j;
	  nodes[1] = i * grid + ((j+1)%grid);
	  nodes[2] = i * grid + ((j+2)%grid);
	  nodes[3] = ((i+1)%grid) * grid + j;
	  nodes[4] = ((i+1)%grid) * grid + ((j+1)%grid);
	  nodes[5] = ((i+1)%grid) * grid + ((j+2)%grid);
	  MC_fill_structure(mc, *next_struct, 6, nodes);
	  if (next_struct->num_neighbors != 10 + (has_extra_node ? 6 : 0))
	    abort();
	  ++next_struct;

	  //  _ 
	  // |_|
	  // |_|
	  nodes[0] = i * grid + j;
	  nodes[1] = i * grid + ((j+1)%grid);
	  nodes[2] = ((i+1)%grid) * grid + j;
	  nodes[3] = ((i+1)%grid) * grid + ((j+1)%grid);
	  nodes[4] = ((i+2)%grid) * grid + j;
	  nodes[5] = ((i+2)%grid) * grid + ((j+1)%grid);
	  MC_fill_structure(mc, *next_struct, 6, nodes);
	  if (next_struct->num_neighbors != 10 + (has_extra_node ? 6 : 0))
	    abort();
	  ++next_struct;
	}
      }
    }
  }

  if (tm.tm_par.entry(MC_tm_par::FeasSolFile).length() > 0) {
     std::ifstream solfile(tm.tm_par.entry(MC_tm_par::FeasSolFile).c_str());
     if (! solfile) {
	throw BCP_fatal_error("Can't open feas solution file!\n");
     }
     int* s = new int[mc.num_nodes];
     for (i = 0; i < mc.num_nodes; ++i) {
	solfile.getline(line, 999);
	sscanf(line, "%i", s+i);
     }
     solfile.close();
     mc.feas_sol = new MC_feas_sol(mc.num_nodes, s, mc.num_edges, mc.edges);
     printf("\nMC: value of input solution: %.0f\n\n", mc.feas_sol->cost);
  }
}