void kMST_ILP::solve()
{
	try {
		// initialize CPLEX
		env = IloEnv();
		model = IloModel( env );

		addTreeConstraints(); // call first, initialises edges

		// add model-specific constraints
		if( model_type == "scf" ) modelSCF();
		else if( model_type == "mcf" ) modelMCF();
		else if( model_type == "mtz" ) modelMTZ();
		else {
			cerr << "No existing model chosen\n";
			exit( -1 );
		}

		addObjectiveFunction();

		// build model
		cplex = IloCplex( model );
		// export model to a text file
		//cplex.exportModel( "model.lp" );
		// set parameters
		setCPLEXParameters();

		// solve model
		cout << "Calling CPLEX solve ...\n";
		cplex.solve();
		cout << "CPLEX finished.\n\n";
		cout << "CPLEX status: " << cplex.getStatus() << "\n";
		cout << "Branch-and-Bound nodes: " << cplex.getNnodes() << "\n";
		cout << "Objective value: " << cplex.getObjValue() << "\n";
		cout << "CPU time: " << Tools::CPUtime() << "\n\n";

		// show result
		IloNumArray edgesSelected(env, edges.getSize()), flowRes(env, edges.getSize()), uRes(env, instance.n_nodes);
		cplex.getValues(edgesSelected, edges);

		if (model_type == "scf") {
			cplex.getValues(flowRes, flow_scf);
		} else if (model_type == "mtz") {
			try {
				cplex.getValues(uRes, u);
			} catch ( IloException& e ) {
				cerr << "Exception while extracting u: " << e << endl;
				uRes = IloNumArray(env, 0);
			}
		}
		cout << "Edges:\n";

		for (unsigned int i=0; i<edges.getSize(); i++) {

			// skip unused ones
			if (((int)edgesSelected[i])  == 0 ) {
				continue;
			}

			if (i == instance.n_edges) {
				cout << endl;
			}
			bool direction = ( i >= instance.n_edges);
			cout << "  " << setw(4) <<  i << ": " << ((int)edgesSelected[i]) << " ";

			// flow
			if (model_type == "scf") {
				cout << "f: " << setw(2) << (flowRes[i]);
			} else if (model_type == "mtz") {
				if (uRes.getSize() != 0) {
					cout << "u: " ;
					if (i < instance.n_edges) {
						//cout << setw(2) << instance.edges[i % instance.n_edges].v1 << ": ";
						cout << setw(2) <<((int)uRes[ instance.edges[i % instance.n_edges].v1]) << " ";
						//cout << setw(2) << instance.edges[i % instance.n_edges].v2 << ": ";
						cout << setw(2) << ((int)uRes[ instance.edges[i % instance.n_edges].v2]) ;
					}  else {
						//cout << setw(2) << instance.edges[i % instance.n_edges].v2 << ": ";
						cout << setw(2) <<((int)uRes[ instance.edges[i % instance.n_edges].v2]) << " ";
						//cout << setw(2) << instance.edges[i % instance.n_edges].v1 << ": ";
						cout << setw(2) <<((int)uRes[ instance.edges[i % instance.n_edges].v1]) ;
					}
				}

			}

			cout << " " << Tools::edgeToString(instance.edges[i % instance.n_edges], direction) ;

			cout << endl;
		}

	} catch (IloAlgorithm::CannotExtractException& e) {
		cerr << "CannotExtractException: " << e << endl ;
		IloExtractableArray failed = e.getExtractables();
		for (IloInt i = 0; i < failed.getSize(); ++i) {
			cerr << "\t" << failed[i] << std::endl;
		}
	} catch( IloException& e ) {
		cerr << "kMST_ILP: exception " << e << "\n";
		exit( -1 );
	}
	catch( ... ) {
		cerr << "kMST_ILP: unknown exception.\n";
		exit( -1 );
	}
}
Exemple #2
0
Variables *kMST_ILP::modelMCF()
{
	MCFVariables *v = new MCFVariables();

	/***** generic part ***/

	const vector<Instance::Edge> edges = directed_edges(instance.edges);
	const u_int n_edges = edges.size();

	/* $x_{ij} \in \{0, 1\}$ variables denote whether edge (i, j) is active. */
	v->xs = createVarArrayXs(env, edges, n_edges);

	/* $v_i \in \{0, 1\}$ variables denote whether node i is active. */
	v->vs = createVarArrayVs(env, instance.n_nodes);

	/* add objective function */
	addObjectiveFunction(env, model, v->xs, edges, n_edges);

	/* There are exactly k - 1 edges not counting edges from the artificial root node 0. */
	addConstraint_k_minus_one_active_edges(env,model,v->xs,edges,n_edges,this->k);

    /* Exactly one node is chosen as the tree root. */
	addConstraint_one_active_outgoing_arc_for_node_zero(env,model,v->xs,edges,n_edges);
 
    /* No edge leads back to the artificial root node 0. */
	addConstraint_no_active_incoming_arc_for_node_zero(env,model,v->xs,edges,n_edges);

	IloExprArray e_in_degree = createExprArray_in_degree(env, edges, n_edges, v->xs, instance);
	IloExprArray e_out_degree = createExprArray_out_degree(env, edges, n_edges, v->xs, instance);

	/* Inactive nodes have no outgoing active edges, active ones at most k - 1. TODO: A tighter bound is to take the sum of incoming goods - 1.*/
	addConstraint_bound_on_outgoing_arcs(model,v->vs,e_out_degree,instance,this->k);

	/* Active nodes have at least one active arc.*/
	addConstraint_active_node_at_least_one_active_arc(model,v->vs,e_in_degree, e_out_degree,instance);
	
	/* Exactly one incoming edge for an active node and none for an inactive node (omitting artificial root). */
 	addConstraint_in_degree_one_for_active_node_zero_for_inactive(model,v->vs,e_in_degree,instance);
	
	//note: position matters. Tried worse positions than this one 
	/* $\sum_{i > 0} v_i = k$. Ensure that exactly k nodes are active. */
	addConstraint_k_nodes_active(env, model, v->vs, instance, this->k);
	e_in_degree.endElements();
	e_out_degree.endElements();


    /***** MCF specific part ***/

	/* $f^k_{ij} \in \{0, 1\}$ variables denote the flow on edge (i, j) for commodity k. */
	for (u_int i = 0; i < instance.n_nodes; i++) {
		v->fss.push_back(IloBoolVarArray(env, n_edges));
	}
	for (u_int k = 0; k < n_edges; k++) {
		const u_int i = edges[k].v1;
		const u_int j = edges[k].v2;
		for (u_int l = 0; l < (u_int) instance.n_nodes; l++) {
			v->fss[l][k] = IloBoolVar(env, Tools::indicesToString("f", l, i, j).c_str());
		}
	}
	
	/* 
     * Each commodity l is generated once by the artificial root node if node l is active, not at all otherwise:
	 * $\forall l \in \{1, \ldots, n\}: \sum_{j:j>0,(0,j) \in A} f^l_{0j} == v_l$ 
     */
	for (u_int c = 1; c < instance.n_nodes; c++){
		IloExpr e_one_commodity(env);		
		for (u_int m = 0; m < n_edges; m++) {
			const u_int i = edges[m].v1;
			const u_int j = edges[m].v2;
			if (i == 0 && j > 0){
				e_one_commodity += v->fss[c][m];	
			}
		} 
		model.add(e_one_commodity == v->vs[c]);
		e_one_commodity.end();
	}

	/* 
     * The artifical root generates k commodities:
     * $\forall l \in \{0,\ldots,n\}\sum_{j:j>0,(0,j) \in A} f^l_{0j} = k$. 
     */
    IloExpr e_root_generates_k(env);		
	for (u_int c = 0; c < instance.n_nodes; c++){
		for (u_int m = 0; m < n_edges; m++) {
			const u_int i = edges[m].v1;
			const u_int j = edges[m].v2;
			if (i == 0 && j > 0){
				e_root_generates_k += v->fss[c][m];	
			}
		} 
	}
	model.add(e_root_generates_k == this->k);   
	e_root_generates_k.end();


	/*
     * No commodity is generated for the artificial root:
     * $\forall i, j: f^0_{ij} = 0$. 
     */ 
	for (u_int m = 0; m < n_edges; m++) {
		model.add(v->fss[0][m] == 0);
	} 

	/* 
	 * Transmitted commodities end up at the target node:
	 * $\forall l>0: \sum_i f^l_{il} = \sum_j f^l_{0j}$. (here: = v_l) 
     */
	for (u_int c = 1; c < (u_int) instance.n_nodes; c++){
		IloExpr e_commodity_reaches_target(env);		
		for (u_int m = 0; m < n_edges; m++) {
			const u_int i = edges[m].v1;
			const u_int j = edges[m].v2;
			if (i != c && j == c){
				e_commodity_reaches_target += v->fss[c][m];	
			}
		} 
		model.add(e_commodity_reaches_target == v->vs[c]);
		e_commodity_reaches_target.end();
	}


	/* 
	 * Once reached, the commodity never leaves the target node:
	 * $\forall l>0: \sum_j f^l_{lj} = 0$.  
     */
	for (u_int c = 1; c < (u_int) instance.n_nodes; c++){
		IloExpr e_commodity_stays_at_target(env);		
		for (u_int m = 0; m < n_edges; m++) {
			const u_int i = edges[m].v1;
			const u_int j = edges[m].v2;
			if (i == c && j != c){
				e_commodity_stays_at_target += v->fss[c][m];	
			}
		} 
		model.add(e_commodity_stays_at_target == 0);
		e_commodity_stays_at_target.end();
	}


	/*
	 * Flow is conserved when not at target node. 
	 * $\forall j, l s.t. j \neq l: \sum_i f^l_{ij} = \sum_i f^l_{ji}$. 
	 */
	 for (u_int c = 0; c < (u_int) instance.n_nodes; c++){
		IloExprArray e_in_flow(env, instance.n_nodes);		
		IloExprArray e_out_flow(env, instance.n_nodes);		
		for (u_int m = 0; m < instance.n_nodes; m++){
			e_in_flow[m] = IloExpr(env);
			e_out_flow[m] = IloExpr(env);
		}
		for (u_int m = 0; m < n_edges; m++) {
			const u_int i = edges[m].v1;
			const u_int j = edges[m].v2;
			e_out_flow[i] += v->fss[c][m];	
			e_in_flow[j] += v->fss[c][m];	
		} 
		for (u_int m = 1; m < instance.n_nodes; m++){
			if (m != c) {
				model.add(e_in_flow[m] == e_out_flow[m]);
			}
		}
		e_in_flow.endElements();
		e_out_flow.endElements();
	 }

	/* 
	 * Commodities may only be transmitted on active edges:
	 * $\forall l, i, j: f^l_{ij} \leq x_{ij}$. 
     */
	for (u_int c = 0; c < (u_int) instance.n_nodes; c++){
		for (u_int m = 0; m < n_edges; m++) {
			model.add(v->fss[c][m] <= v->xs[m]);
		} 
	}

	/* 
	 * For each commodity l , the total flow is <= k if node l is active, 0 otherwise
	 * (works well for all before g05, k=n/2 which is a bit slower with this)
     */
	for (u_int c = 1; c < (u_int) instance.n_nodes; c++){
		IloExpr e_total_flow(env);		
		for (u_int m = 0; m < n_edges; m++) {
			e_total_flow += v->fss[c][m];	
		} 
		model.add(e_total_flow <= this->k * v->vs[c]);
		e_total_flow.end();
	}
	return v;
}
Exemple #3
0
Variables *kMST_ILP::modelMTZ()
{
	MTZVariables *v = new MTZVariables();

    /***** generic part ***/

	const vector<Instance::Edge> edges = directed_edges(instance.edges);
	const u_int n_edges = edges.size();

	/* $x_{ij} \in \{0, 1\}$ variables denote whether edge (i, j) is active. */
	v->xs = createVarArrayXs(env, edges, n_edges);

	/* $v_i \in \{0, 1\}$ variables denote whether node i is active. */
	v->vs = createVarArrayVs(env, instance.n_nodes);

	/* add objective function */
	addObjectiveFunction(env, model, v->xs, edges, n_edges);

	/* There are exactly k - 1 edges not counting edges from the artificial root node 0. */
	addConstraint_k_minus_one_active_edges(env,model,v->xs,edges,n_edges,this->k);

    /* Exactly one node is chosen as the tree root. */
	addConstraint_one_active_outgoing_arc_for_node_zero(env,model,v->xs,edges,n_edges);
 
    /* No edge leads back to the artificial root node 0. */
	addConstraint_no_active_incoming_arc_for_node_zero(env,model,v->xs,edges,n_edges);

	IloExprArray e_in_degree = createExprArray_in_degree(env, edges, n_edges, v->xs, instance);
	IloExprArray e_out_degree = createExprArray_out_degree(env, edges, n_edges, v->xs, instance);

	/* Inactive nodes have no outgoing active edges, active ones at most k - 1. TODO: A tighter bound is to take the sum of incoming goods - 1.*/
	addConstraint_bound_on_outgoing_arcs(model,v->vs,e_out_degree,instance,this->k);

	/* Active nodes have at least one active arc.*/
	addConstraint_active_node_at_least_one_active_arc(model,v->vs,e_in_degree, e_out_degree,instance);
	
	/* Exactly one incoming edge for an active node and none for an inactive node (omitting artificial root). */
 	addConstraint_in_degree_one_for_active_node_zero_for_inactive(model,v->vs,e_in_degree,instance);
	
	//note: position matters. Tried worse positions than this one 
	/* $\sum_{i > 0} v_i = k$. Ensure that exactly k nodes are active. */
	addConstraint_k_nodes_active(env, model, v->vs, instance, this->k);
	e_in_degree.endElements();
	e_out_degree.endElements();


    /***** MTZ specific part ***/

	/* $u_i \in [0, k]$ variables are used to impose an order on nodes. */
	v->us = IloIntVarArray(env, instance.n_nodes);
	for (u_int i = 0; i < instance.n_nodes; i++) {
		v->us[i] = IloIntVar(env, 0, k, Tools::indicesToString("u", i).c_str());
	}

	
	IloExpr e3(env);
	e3 += v->us[0];
	/* $u_0 = 0$. Set level of artificial root 0 to 0. */
	model.add(e3 == 0);
	e3.end();

	for (u_int k = 0; k < n_edges; k++) {
		const u_int i = edges[k].v1;
		const u_int j = edges[k].v2;

		IloExpr e4(env);
		e4 = v->us[i] + v->xs[k] - v->us[j] - (-v->xs[k] + 1) * this->k;
		/* $\forall i, j: u_i + x_{ij} \leq u_j + (1 - x_{ij})k$. 
		 * Enforce order hierarchy on nodes. */
		model.add(e4 <= 0);
		e4.end();
	}

	for (u_int i = 0; i < instance.n_nodes; i++) {
		/* $\forall i: u_i <= nv_i$ force order of inactive nodes to 0 */
		/* helps with big instances 6,7,8 */
		model.add(v->us[i] <= v->vs[i] * (int) instance.n_nodes);
	}	
	return v;
}
Exemple #4
0
Variables *kMST_ILP::modelSCF()
{
	SCFVariables *v = new SCFVariables();

	const vector<Instance::Edge> edges = directed_edges(instance.edges);
	const u_int n_edges = edges.size();

	/* $x_{ij} \in \{0, 1\}$ variables denote whether edge (i, j) is active. */
	v->xs = createVarArrayXs(env, edges, n_edges);

	/* $v_i \in \{0, 1\}$ variables denote whether node i is active. */
	v->vs = createVarArrayVs(env, instance.n_nodes);

	/* add objective function */
	addObjectiveFunction(env, model, v->xs, edges, n_edges);

	/* There are exactly k - 1 edges not counting edges from the artificial root node 0. */
	addConstraint_k_minus_one_active_edges(env,model,v->xs,edges,n_edges,this->k);

    /* Exactly one node is chosen as the tree root. */
	addConstraint_one_active_outgoing_arc_for_node_zero(env,model,v->xs,edges,n_edges);
 
    /* No edge leads back to the artificial root node 0. */
	addConstraint_no_active_incoming_arc_for_node_zero(env,model,v->xs,edges,n_edges);

	IloExprArray e_in_degree = createExprArray_in_degree(env, edges, n_edges, v->xs, instance);
	IloExprArray e_out_degree = createExprArray_out_degree(env, edges, n_edges, v->xs, instance);

	/* Inactive nodes have no outgoing active edges, active ones at most k - 1. TODO: A tighter bound is to take the sum of incoming goods - 1.*/
	addConstraint_bound_on_outgoing_arcs(model,v->vs,e_out_degree,instance,this->k);

	/* Active nodes have at least one active arc.*/
	addConstraint_active_node_at_least_one_active_arc(model,v->vs,e_in_degree, e_out_degree,instance);
	
	/* Exactly one incoming edge for an active node and none for an inactive node (omitting artificial root). */
 	addConstraint_in_degree_one_for_active_node_zero_for_inactive(model,v->vs,e_in_degree,instance);
	
	//note: position matters. Tried worse positions than this one 
	/* $\sum_{i > 0} v_i = k$. Ensure that exactly k nodes are active. */
	addConstraint_k_nodes_active(env, model, v->vs, instance, this->k);
	e_in_degree.endElements();
	e_out_degree.endElements();

	/* $f_{ij} \in [0, k - 1]$ variables denote the number of goods on edge (i, j). */
	v->fs = createVarArrayFs(env, edges, n_edges);

	IloExprArray e_in_flow = createExprArray_in_flow(env, edges, n_edges, v->fs, instance);
	IloExprArray e_out_flow = createExprArray_out_flow(env, edges, n_edges, v->fs, instance);

	/* 
	 * Active nodes consume exactly 1 commodity, inactive nodes conserve flow.
	 * $\forall i \neq 0: \sum_j (f_{ji} - f_{ij}) == v_i$ 
     */
	for (u_int i = 0; i < instance.n_nodes; i++) {
		if (i == 0){
			/* Don't add a constraint for the artificial root. */
		} else if (i > 0) {
			/* outflow = inflow -1 for active nodes, same for inactive nodes. */
			model.add(v->vs[i] == e_in_flow[i] - e_out_flow[i]);
		}
	}
	e_in_flow.endElements();
	e_out_flow.endElements();


	/* $\forall i, j \neq 0: f_{ij} \leq kx_{ij}$. Only active edges transport goods.
	 * TODO: bound sum of incoming goods per node.
	 * $\forall i, j s.t. i or j is 0: f_{ij} = kx_{ij}$. Only a single edge
	 * incident on the artificial root transports goods. */

	for (u_int k = 0; k < n_edges; k++) {
		const u_int i = edges[k].v1;
		const u_int j = edges[k].v2;
		if (i == 0 || j == 0) {
			model.add(v->fs[k] == this->k * v->xs[k]);
		} else {  
			model.add(v->fs[k] <= (this->k) * v->xs[k]);
		}
	}

	return v;
}