Exemplo n.º 1
0
// Start the optimization
SolverResult SolverGurobi::runOptimizer()
{
    if (!getInitialized())
        initialize();

    try
    {
        // Create Gurobi environment and set parameters
        GRBEnv env = GRBEnv();
        env.set(GRB_IntParam_OutputFlag, 0);

        GRBModel model = GRBModel(env);

        // Get problem info
        int numVars = constraints->getNumVariables();
        int numConstraints = constraints->getNumConstraints();

        // Get variables
        auto variables = constraints->getVariables();

        // Create array of model variables
        GRBVar vars[numVars];
        for (int i = 0; i < numVars; i++)
        {
            //vars[i] = model.addVar(0.0, 1.0, 0.0, GRB_BINARY);

            // Set variable type
            char type = GRB_CONTINUOUS;
            if (variables.at(i)->getType() == VariableType::BINARY)
            {
                type = GRB_BINARY;
            }
            else if (variables.at(i)->getType() == VariableType::INTEGER)
            {
                type = GRB_INTEGER;
            }
            vars[i] = model.addVar(variables.at(i)->getLowerBound(),
                                   variables.at(i)->getUpperBound(),
                                   variables.at(i)->getCost(),
                                   type);
        }

        // Integrate variables into model
        model.update();

        // Set starting points (does not help much...)
        for (int i = 0; i < numVars; i++)
            vars[i].set(GRB_DoubleAttr_Start, variables.at(i)->getValue());

        /*
         * Add constraints Ax <= b (or Ax = b)
         * by evaluating gradient and build A matrix
         */
        DenseVector x = DenseVector::Zero(numVars);
        DenseVector dx = constraints->evalJacobian(x);

        // Get constraint bounds
        std::vector<double> clb;
        std::vector<double> cub;
        constraints->getConstraintBounds(clb,cub);

        std::vector<int> rowGradient, colGradient;
        constraints->structureJacobian(rowGradient, colGradient);
        int nnzJacobian = constraints->getNumNonZerosJacobian();

        // Add constraints one row at the time
        for (int row = 0; row < numConstraints; row++)
        {
            // Build constraint
            GRBLinExpr expr = 0;

            // Loop through all non-zeros (inefficient)
            for (int i = 0; i < nnzJacobian; i++)
            {
                if (rowGradient.at(i) == row)
                {
                    int j = colGradient.at(i);
                    expr += dx(i)*vars[j];
                }
            }

            // Add constraint to model
            if (clb.at(row) == cub.at(row))
            {
                model.addConstr(expr, GRB_EQUAL, cub.at(row));
            }
            else
            {
                model.addConstr(expr, GRB_LESS_EQUAL, cub.at(row));
            }
        }

        // More efficient method - avoids dense matrix
//        std::vector<int> rows = {1,1,1,2,2,3,4,4,4,4,4,5};
//        std::vector<int>::iterator start,stop;
//        start = rows.begin();
//        stop = start;
//        while (start != rows.end())
//        {
//            while (stop != rows.end())
//            {
//                if (*stop == *start)
//                    ++stop;
//                else
//                    break;
//            }
//            for (std::vector<int>::iterator it = start; it != stop; ++it)
//                cout << *it << endl;

//            start = stop;
//        }

        model.update();

        assert(numVars == model.get(GRB_IntAttr_NumVars));
        assert(numConstraints == model.get(GRB_IntAttr_NumConstrs));

        // Optimize model
        model.optimize();

        // Check status
        int optimstatus = model.get(GRB_IntAttr_Status);

        if (optimstatus == GRB_INF_OR_UNBD)
        {
            model.getEnv().set(GRB_IntParam_Presolve, 0);
            model.optimize();
            optimstatus = model.get(GRB_IntAttr_Status);
        }

        // Create result object
        SolverResult result(SolverStatus::ERROR, INF, std::vector<double>(numVars,0));

        // Check Gurobi status
        if (optimstatus == GRB_OPTIMAL)
        {
            result.status = SolverStatus::OPTIMAL;

            // Get solution info
            result.objectiveValue = model.get(GRB_DoubleAttr_ObjVal);

            std::vector<double> optimalSolution;
            for (int i = 0; i < numVars; i++)
            {
                optimalSolution.push_back(vars[i].get(GRB_DoubleAttr_X));
            }

            result.primalVariables = optimalSolution;

            /*
             * Reduced costs and constraint duals are
             * only available for continuous models
             */
            std::vector<double> reducedCosts;
            std::vector<double> constraintDuals;
            if (!model.get(GRB_IntAttr_IsMIP))
            {
                for (int i = 0; i < numVars; i++)
                {
                    // Get reduced costs (related to range constraint duals)
                    reducedCosts.push_back(vars[i].get(GRB_DoubleAttr_RC));
                }

                for (int i = 0; i < numConstraints; i++)
                {
                    GRBConstr c = model.getConstr(i);
                    double pi = c.get(GRB_DoubleAttr_Pi);
                    constraintDuals.push_back(pi);
                }
            }

            result.lowerBoundDualVariables = reducedCosts;
            result.upperBoundDualVariables = reducedCosts;
            result.constraintDualVariables = constraintDuals;

            return result;
        }
        else if (optimstatus == GRB_INFEASIBLE)
        {
            result.status = SolverStatus::INFEASIBLE;
            result.objectiveValue = INF;
            // compute and write out IIS
            // model.computeIIS();
            // model.write("problem.lp");
            return result;
        }
        else if (optimstatus == GRB_UNBOUNDED)
        {
            result.status = SolverStatus::UNBOUNDED;
            result.objectiveValue = -INF;
            return result;
        }
        else
        {
            result.status = SolverStatus::ERROR;
            result.objectiveValue = INF;
            return result;
        }
    }
    catch(GRBException e)
    {
        cout << "SolverGurobi: Error code = " << e.getErrorCode() << endl;
        cout << e.getMessage() << endl;
        return SolverResult(SolverStatus::ERROR, INF, std::vector<double>(constraints->getNumVariables(),0));
    }
    catch (...)
    {
        cout << "SolverGurobi: Error during optimization!" << endl;
        return SolverResult(SolverStatus::ERROR, INF, std::vector<double>(constraints->getNumVariables(),0));
    }
}
int main(){

	
	
	
	float peso1, peso2, peso3,peso4;
	int origem, destino; // vértices para cada aresta;
	int id = 0; // id das arestas que leremos do arquivo para criar o grafo
	cin>>n; // quantidade de vértices do grafo;
	arestas = new short*[n];
	coeficienteObjetv = new double*[n];
	matrix_peso1 = new double*[n];
	matrix_peso2 = new double*[n];
	matrix_peso3 = new double*[n];
	matrix_peso4 = new double*[n];
	for (int i=0; i<n; i++){
		arestas[i] = new short[n];
		coeficienteObjetv[i] = new double[n];
		matrix_peso1[i] = new double[n];
		matrix_peso2[i] = new double[n];
		matrix_peso3[i] = new double[n];
		matrix_peso4[i] = new double[n];
	}


	GRBEnv env = GRBEnv();;
	env.set("OutputFlag","0");
	GRBModel model = GRBModel(env);;

	GRBVar **y, **x;

	float epslon = 0.0001;
	//cin>>epslon;



  	y = new GRBVar*[n]; 
   	x = new GRBVar*[n];


   	for (int i=0; i<n;i++){
        y[i] = new GRBVar[n];
        x[i] = new GRBVar[n];
   	}

	int constrCont=0;
    // Create variables

	for (int i=0; i<n; i++){
       for (int j=0; j<n; j++){
       	arestas[i][j] = 0;
       }
   }

	while (cin>>origem){
		cin>>destino;
		cin>>peso1;
		cin>>peso2;
		cin>>peso3;
		cin>>peso4;
		coeficienteObjetv[origem][destino] = (peso1*epslon + peso2*epslon + peso3*epslon + peso4)*(-1); // o problema é de maximizacao
		x[origem][destino] = model.addVar(0.0, 100000, 0.0, GRB_CONTINUOUS, "x"+to_string(origem)+to_string(destino));
        x[destino][origem] = model.addVar(0.0, 100000, 0.0, GRB_CONTINUOUS, "x"+to_string(destino)+to_string(origem));
      	y[origem][destino] = model.addVar(0.0, 1.0, 0.0, GRB_BINARY, "y"+to_string(origem)+to_string(destino));
      	arestas[origem][destino] = 1;
      	arestas[destino][origem] = 1;
      	matrix_peso1[origem][destino] = peso1*(-1);
      	matrix_peso2[origem][destino] = peso2*(-1);
      	matrix_peso3[origem][destino] = peso3*(-1);
      	matrix_peso4[origem][destino] = peso4*(-1);
		id++;
	}
	int nA = id; // quantidade de arestas do grafo	

	int m = 1;// por default, o m falado por Lokman and Koksalan sera igual a 1


    model.update();

    // Set objective: 
    GRBLinExpr exprObjet;
    for (int i=0; i<n; i++){
      for (int j=i+1; j<n; j++){
      	if (arestas[i][j] == 1)
       		exprObjet.addTerms(&coeficienteObjetv[i][j], &y[i][j],1);
      }
    }
  
    model.setObjective(exprObjet,GRB_MAXIMIZE); 


    // constraint 3.9 (FERNANDES, 2016)
    GRBLinExpr constr5 ;
    double coefff = 1;
    for (int j=0+1; j<n; j++){
    	if (arestas[0][j] == 1)
        	constr5.addTerms(&coefff,&x[0][j],1);
    }
    model.addConstr(constr5, GRB_EQUAL, n-1,to_string(constrCont++));
  


    // // Add constraint 3.10 (FERNANDES, 2016)
     double com = -1;
    for (int j=1; j<n; j++){
      GRBLinExpr constr2 = 0;
      for (int i=0; i<n; i++){
        if (arestas[i][j] == 1){
          constr2.addTerms(&coefff,&x[i][j],1);
          constr2.addTerms(&com,&x[j][i],1);
        }
      }
      model.addConstr(constr2, GRB_EQUAL, 1,to_string(constrCont++));
    }

    double coef = (double) n - 1;
    for (int i=0; i<n; i++){
      for (int j=i+1; j<n; j++){
      	if (arestas[i][j] == 1){
	        GRBLinExpr constr8;
	        GRBLinExpr constr9;
	        constr8.addTerms(&coef,&y[i][j],1);
	        constr9.addTerms(&coefff  ,&x[i][j],1);
	        constr9.addTerms(&coefff  ,&x[j][i],1);
	      	model.addConstr(constr8, GRB_GREATER_EQUAL, constr9,to_string(constrCont++));
    	}
      }
    }

     //cout<<"Modelo carregado"<<endl;
    for (int i=0; i<n; i++){

      for (int j=i+1; j<n; j++){
      	if (arestas[i][j] == 1){
	        GRBLinExpr constr22;
	        GRBLinExpr constr33;
	        constr22.addTerms(&coefff  ,&y[i][j],1);
	        constr33.addTerms(&coefff  ,&x[i][j],1);
	        constr33.addTerms(&coefff  ,&x[j][i],1);
	       // cout<<constr22<<GRB_LESS_EQUAL<<constr33<<endl;     
	        model.addConstr(constr22, GRB_LESS_EQUAL, constr33,to_string(constrCont++));
    	}
      }
    }

    int nn = 0; // o 'n' do algoritmo de Lokman and Koksalan 	
    //int kk_estrela = 0; // o 'k*' do algoritmo 2 de Lokman and Koksalan
    int MM = 100000000; // o 'M' do algoritmo 2 de Lokman and Koksalan 	
    int z4_k_estrela; // pra guardar o Z_p^(P^(b^(k*,n))) do algoritmo 2 de Lokman and Koksalan
    /* 
	* Algoritmo 2 de Lokman and Koksalan 	
	*/
	try {
		times(&tempsInit);


		// para medir o tempo em caso limite
		pthread_t thread_time; 
		pthread_attr_t attr;
		int nnnnnnnn=0;
		if(pthread_create(&thread_time, NULL, &tempo, (void*)nnnnnnnn)){ // on criee efectivement la thread de rechaufage
	       cout<<"Error to create the thread"<<endl;
	        exit(EXIT_FAILURE);
	    }
	    //


	    bool auxbol = false; // vira true (e o será pra sempre) quando resolvemos um modelo diferente do SIMPLES
		int optimstatus;

		short **result = new short*[n];
		for (int ii=0; ii<n; ii++){
			result[ii] = new short[n];
		}
		
		model.optimize(); // P0,4 --> n=0 (modelo SIMPLES)
		optimstatus = model.get(GRB_IntAttr_Status);
		int z1=0,z2=0,z3=0,z4=0;
		if (optimstatus != GRB_INFEASIBLE){
			for (int i=0; i<n; i++){
				for (int j=i+1; j<n; j++){
				    if (arestas[i][j] == 1){
				        result[i][j] = y[i][j].get(GRB_DoubleAttr_X); // GUARDA O RESULTADO
				    	z1+=result[i][j]*matrix_peso1[i][j]; // calcula os pesos
				    	z2+=result[i][j]*matrix_peso2[i][j];
				    	z3+=result[i][j]*matrix_peso3[i][j];
				    	z4+=result[i][j]*matrix_peso4[i][j];
				    }
				}
			}
			S.push_back(result);
			Pesos ppp = (Pesos){z1,z2,z3,z4};
			Z.push_back(ppp);
			nn++;
		}

		do{ // esse loop para quando z4_k_estrela==-MM
			z4_k_estrela =(-1)*MM; // guarda o maximo
			short **z_n_plus_1 = new short*[n];
			for (int ii=0; ii<n; ii++){
				z_n_plus_1[ii] = new short[n];
			}
			int z1_estrela, z2_estrela, z3_estrela;


			for (int ki=-1; ki<nn; ki++){ // no algoritmo original, ki deve variar de 0 à n (existindo solucoes de 1 à n). 
				//aqui, portanto, fazemos k de -1 à n-1, porque as solucoes vao de 0 à n-1
				for (int kj=-1; kj<nn; kj++){ // como i de ver menor que j, a unica possibilidade é i=1 e j=2, pois p-2=2
	 				//cout<<ki<<" "<<kj<<endl;
	 				//cout<< Z[ki].peso1<<" "<<  Z[kj].peso2<<endl;
	 				//if (kj!=-1 && ki!=-1 && (Z[ki].peso1 + 1 <= Z[kj].peso1)){
	 					//Primeiramente, prepara o b1 e b2 e b3
	 					int b1,b2,b3;
	 					//b1
	 					if (ki==-1) b1=(-1)*MM; // -M
						else {
							b1 = Z[ki].peso1 + 1;
						}

						//b2
	 					if (kj==-1) b2=(-1)*MM; // -M
						else {
							if (Z[kj].peso1 >= b1){
								b2 = Z[kj].peso2 + 1;
							} else b2=(-1)*MM; // -M
						}

						//b3
						b3 = (-1)*MM; // Snk = vazio
						for (int ii=0; ii<S.size(); ii++){
							if (Z[ii].peso1>=b1 && Z[ii].peso2>=b2) {
								if (Z[ii].peso3 > b3){
									b3 = Z[ii].peso3;
								}
							}
						}
						if (b3!=(-1)*MM) b3=b3+1; // max + 1
						//cout <<"b1= "<<b1<<" b2= "<<" "<<b2<<" b3= "<<b3<<endl;
				
						if (auxbol == true){ // remove as restricoes de z2>b2 e adiciona novas
							GRBConstr cb1 = model.getConstrByName("cb1");
							GRBConstr cb2 = model.getConstrByName("cb2");
							GRBConstr cb3 = model.getConstrByName("cb3");
							model.remove(cb1);
							model.remove(cb2);
							model.remove(cb3);
						}

						GRBLinExpr cb1;
						GRBLinExpr cb2;
						GRBLinExpr cb3;
						 for (int i=0; i<n; i++){
						 	for (int j=i+1; j<n; j++){
						 		if (arestas[i][j] == 1){
						 			cb1.addTerms(&matrix_peso1[i][j], &y[i][j],1);
						 			cb2.addTerms(&matrix_peso2[i][j], &y[i][j],1);
						 			cb3.addTerms(&matrix_peso3[i][j], &y[i][j],1);
						 		}
						 	}
						 }
						model.addConstr(cb1, GRB_GREATER_EQUAL, b1,"cb1");
					  	model.addConstr(cb2, GRB_GREATER_EQUAL, b2,"cb2");
					  	model.addConstr(cb3, GRB_GREATER_EQUAL, b3,"cb3");

					  	// AGORA RESOLVE-SE O MODELO
					  	auxbol=true;
					  	model.optimize(); 
						optimstatus = model.get(GRB_IntAttr_Status);
		
						if (optimstatus != GRB_INFEASIBLE){
							short **result = new short*[n];
							for (int ii=0; ii<n; ii++){
								result[ii] = new short[n];
							}
							z1=0,z2=0,z3=0,z4=0;
							for (int i=0; i<n; i++){
								for (int j=i+1; j<n; j++){
						  			if (arestas[i][j] == 1){
						        		result[i][j] = y[i][j].get(GRB_DoubleAttr_X); // GUARDA O RESULTADO
								    	z1+=result[i][j]*matrix_peso1[i][j]; // calcula os pesos
								    	z2+=result[i][j]*matrix_peso2[i][j];
								    	z3+=result[i][j]*matrix_peso3[i][j];
								    	z4+=result[i][j]*matrix_peso4[i][j];
						    		}
								}
							}
							if (z4>z4_k_estrela){
								z1_estrela = z1;
								z2_estrela = z2;
								z3_estrela = z3;
								z4_k_estrela = z4;
								for (int i=0; i<n; i++){
									for (int j=i+1; j<n; j++){
										z_n_plus_1[i][j] = result[i][j];
									}
								}
							}
						}
	 				//}
				}	
			}
			if (z4_k_estrela!=(-1)*MM){
				S.push_back(z_n_plus_1);
				Pesos pppp = (Pesos){z1_estrela,z2_estrela,z3_estrela,z4_k_estrela};
				Z.push_back(pppp);
				nn++;
				//cout<<"nn =  "<<nn<<endl;
			}
		} while (z4_k_estrela!=(-1)*MM);
				
				
	  	 	times(&tempsFinal1);   /* current time */ // clock final
			clock_t user_time1 = (tempsFinal1.tms_utime - tempsInit.tms_utime);
			cout<<user_time1<<endl;
			cout<<(float) user_time1 / (float) sysconf(_SC_CLK_TCK)<<endl;//"Tempo do usuario por segundo : "
   	


			cout<<"RESULTADO FINAL..."<<endl;
		   	printResultado();

  	 	} catch(GRBException e) {
	    cout << "Error code = " << e.getErrorCode() << endl;
	    cout << e.getMessage() << endl;
	  } catch(...) {
	    cout << "Exception during optimization" << endl;
	  }
   	
	return 0;
}
Exemplo n.º 3
0
int main (int argc, char * argv[]) {
    chrono :: steady_clock :: time_point tBegin = chrono :: steady_clock :: now();
    string I ("0");
    ulint timeLimit = 10;

    if (argc >= 2) {
        I = string (argv[1]);
    }

    if (argc >= 3) {
        timeLimit = atoi(argv[2]);
    }

    ulint nComplete, k, t, n, m, root;
    double d;

    cin >> nComplete >> d >> k >> t >> n >> m >> root;

    vector <ulint> penalty (nComplete); // vector with de penalties of each vectex
    vector < list < pair <ulint, ulint> > > adj (nComplete); // adjacency lists for the graph

    for (ulint v = 0; v < nComplete; v++) {
        cin >> penalty[v];
    }

    vector <ulint> solutionV (nComplete, 0);

    // reading solution vertices
    for (ulint i = 0; i < n; i++) {
        ulint v;
        cin >> v;
        solutionV[v] = 1;
    }

    vector < pair < pair <ulint, ulint> , ulint> > E (m); // vector of edges with the format ((u, v), w)
    map < pair <ulint, ulint>, ulint> mE; // map an edge to its ID
    vector < vector <ulint> > paths (m);

    // reading graph
    for (ulint e = 0; e < m; e++) {
        ulint u, v, w, pathSize;
        cin >> u >> v >> w >> pathSize;
        adj[u].push_back(make_pair(v, w));
        adj[v].push_back(make_pair(u, w));
        E[e] = make_pair(make_pair(u, v), w);
        mE[make_pair(u, v)] = e;
        mE[make_pair(v, u)] = e;
        paths[e] = vector <ulint> (pathSize);
        for (ulint i = 0; i < pathSize; i++) {
            cin >> paths[e][i];
        }
    }

    try {
        string N = itos(nComplete);
        stringstream ssD;
        ssD << fixed << setprecision(1) << d;
        string D = ssD.str();
        D.erase(remove(D.begin(), D.end(), '.'), D.end());
        string K = itos(k);
        string T = itos(t);

        ifstream remainingTimeFile ("./output/N" + N + "D" + D + "K" + K + "T" + T + "I" + I + "/remainingTime.txt");
        lint remainingTime = 0;
        if (remainingTimeFile.is_open()) {
            remainingTimeFile >> remainingTime;
        }
        if (remainingTime > 0) {
            timeLimit += remainingTime;
        }

        GRBEnv env = GRBEnv();

        env.set(GRB_IntParam_LazyConstraints, 1);
        env.set(GRB_IntParam_LogToConsole, 0);
        env.set(GRB_StringParam_LogFile, "./output/N" + N + "D" + D + "K" + K + "T" + T + "I" + I + "/log2.txt");
        env.set(GRB_DoubleParam_TimeLimit, ((double) timeLimit));

        GRBModel model = GRBModel(env);

        model.getEnv().set(GRB_IntParam_LazyConstraints, 1);
        model.getEnv().set(GRB_IntParam_LogToConsole, 0);
        model.getEnv().set(GRB_StringParam_LogFile, "./output/N" + N + "D" + D + "K" + K + "T" + T + "I" + I + "/log2.txt");
        model.getEnv().set(GRB_DoubleParam_TimeLimit, ((double) timeLimit));

        vector <GRBVar> y (nComplete);

        // ∀ v ∈ V
        for (ulint v = 0; v < nComplete; v++) {
            // y_v ∈ {0.0, 1.0}
            y[v] = model.addVar(0.0, 1.0, 0.0, GRB_BINARY, "y_" + itos(v));
        }

        vector <GRBVar> x (m);

        // ∀ e ∈ E
        for (ulint e = 0; e < m; e++) {
            ulint u, v;
            u = E[e].first.first;
            v = E[e].first.second;
            // y_e ∈ {0.0, 1.0}
            x[e] = model.addVar(0.0, 1.0, 0.0, GRB_BINARY, "x_" + itos(u) + "_" + itos(v));
        }

        model.update();

        GRBLinExpr obj = 0.0;

        // obj = ∑ ce * xe
        for (ulint e = 0; e < m; e++) {
            ulint w;
            w = E[e].second;
            obj += w * x[e];
        }

        // obj += ∑ πv * (1 - yv)
        for (ulint v = 0; v < nComplete; v++) {
            obj += penalty[v] * (1.0 - y[v]);
        }

        model.setObjective(obj, GRB_MINIMIZE);

        // yu == 1
        model.addConstr(y[root] == 1.0, "c_0");

        // dominance
        // ∀ v ∈ V
        for (ulint v = 0; v < nComplete; v++) {
            if (solutionV[v] == 1) {
                GRBLinExpr constr = 0.0;
                constr += y[v];
                model.addConstr(constr == 1, "c_1_" + itos(v));
            }
        }

        // each vertex must have exactly two edges adjacent to itself
        // ∀ v ∈ V
        for (ulint v = 0; v < nComplete; v++) {
            // ∑ xe == 2 * yv , e ∈ δ({v})
            GRBLinExpr constr = 0.0;
            for (list < pair <ulint, ulint> > :: iterator it = adj[v].begin(); it != adj[v].end(); it++) {
                ulint w = (*it).first; // destination
                ulint e = mE[make_pair(v, w)];
                constr += x[e];
            }
            model.addConstr(constr == 2.0 * y[v], "c_2_" + itos(v));
        }

        subtourelim cb = subtourelim(y, x, nComplete, m, E, mE, root);
        model.setCallback(&cb);

        model.optimize();

        if (model.get(GRB_IntAttr_SolCount) > 0) {
            ulint solutionCost = 0;
            set <ulint> solutionVectices;
            vector < pair <ulint, ulint> > solutionEdges;
            solutionCost = round(model.get(GRB_DoubleAttr_ObjVal));
            for (ulint v = 0; v < nComplete; v++) {
                if (y[v].get(GRB_DoubleAttr_X) >= 0.5) {
                    solutionVectices.insert(v);
                }
            }
            for (ulint e = 0; e < m; e++) {
                if (x[e].get(GRB_DoubleAttr_X) >= 0.5) {
                    for (ulint i = 0; i < paths[e].size() - 1; i++) {
                        pair <ulint, ulint> edge;
                        if (paths[e][i] < paths[e][i + 1]) {
                            edge.first = paths[e][i];
                            edge.second = paths[e][i + 1];
                        } else {
                            edge.first = paths[e][i + 1];
                            edge.second = paths[e][i];
                        }
                        solutionEdges.push_back(edge);
                    }
                }
            }
            cout << solutionVectices.size() << ' ' << solutionEdges.size() << ' ' << solutionCost << endl;
            for (set <ulint> :: iterator it = solutionVectices.begin(); it != solutionVectices.end(); it++) {
                ulint v = *it;
                cout << v << endl;
            }
            for (vector < pair <ulint, ulint> > :: iterator it = solutionEdges.begin(); it != solutionEdges.end(); it++) {
                pair <ulint, ulint> e = *it;
                cout << e.first << " " << e.second << endl;
            }
        } else {
            cout << "0 0 0" << endl;
        }

        // exporting model
        model.write("./output/N" + N + "D" + D + "K" + K + "T" + T + "I" + I + "/model2.lp");

        ofstream objValFile ("./output/N" + N + "D" + D + "K" + K + "T" + T + "I" + I + "/objVal2.txt", ofstream :: out);
        objValFile << model.get(GRB_DoubleAttr_ObjVal);
        objValFile.close();

        ofstream gapFile ("./output/N" + N + "D" + D + "K" + K + "T" + T + "I" + I + "/gap2.txt", ofstream :: out);
        gapFile << model.get(GRB_DoubleAttr_MIPGap);
        gapFile.close();

        chrono :: steady_clock :: time_point tEnd = chrono :: steady_clock :: now();
        chrono :: nanoseconds elapsedTime = chrono :: duration_cast <chrono :: nanoseconds> (tEnd - tBegin);
        ofstream elapsedTimeFile ("./output/N" + N + "D" + D + "K" + K + "T" + T + "I" + I + "/elapsedTime2.txt", ofstream :: out);
        elapsedTimeFile << elapsedTime.count();
        elapsedTimeFile.close();
    } catch (GRBException e) {