Esempio n. 1
0
    static void Main(string[] args)
    {
        if (args.Length < 1)
        {
            Console.Out.WriteLine("Usage: lpmethod_cs filename");
            return;
        }

        try {
            // Read model
            GRBEnv   env   = new GRBEnv();
            GRBModel model = new GRBModel(env, args[0]);
            GRBEnv   menv  = model.GetEnv();

            // Solve the model with different values of Method
            int    bestMethod = -1;
            double bestTime   = menv.Get(GRB.DoubleParam.TimeLimit);
            for (int i = 0; i <= 2; ++i)
            {
                model.Reset();
                menv.Set(GRB.IntParam.Method, i);
                model.Optimize();
                if (model.Get(GRB.IntAttr.Status) == GRB.Status.OPTIMAL)
                {
                    bestTime   = model.Get(GRB.DoubleAttr.Runtime);
                    bestMethod = i;
                    // Reduce the TimeLimit parameter to save time
                    // with other methods
                    menv.Set(GRB.DoubleParam.TimeLimit, bestTime);
                }
            }

            // Report which method was fastest
            if (bestMethod == -1)
            {
                Console.WriteLine("Unable to solve this model");
            }
            else
            {
                Console.WriteLine("Solved in " + bestTime
                                  + " seconds with Method: " + bestMethod);
            }

            // Dispose of model and env
            model.Dispose();
            env.Dispose();
        } catch (GRBException e) {
            Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
        }
    }
        /// <summary>
        ///     Configures a <see cref="GurobiRunner" /> using the given parameters.
        /// </summary>
        /// <param name="parameters">The parameters to configure the Gurobi run with.</param>
        /// <returns>The configured <see cref="GurobiRunner" />.</returns>
        public GurobiRunner ConfigureTargetAlgorithm(Dictionary <string, IAllele> parameters)
        {
            // Create a new gurobi environment.
            var gurobiEnvironment = new GRBEnv();

            // Add all simple, non-artificial parameters to it.
            foreach (var param in parameters)
            {
                gurobiEnvironment.Set(param.Key, param.Value.ToString());
            }

            // Log which settings will be used for the next Gurobi run(s).
            LoggingHelper.WriteLine(VerbosityLevel.Info, string.Join(", ", parameters.Select(parameter => $"{parameter.Key}: {parameter.Value}")));

            gurobiEnvironment.Threads = this.GurobiSettings.ThreadCount;

            gurobiEnvironment.NodefileStart = this.GurobiSettings.NodefileStartSizeGigabyte;

            if (!Directory.Exists(this.GurobiSettings.NodefileDirectory.FullName))
            {
                this.GurobiSettings.NodefileDirectory.Create();
            }

            gurobiEnvironment.NodefileDir = this.GurobiSettings.NodefileDirectory.FullName;

            // Return the new gurobi runner.
            return(new GurobiRunner(gurobiEnvironment, this.GurobiSettings));
        }
        void BuildModel_SubProblem1()
        {
            _env = new GRBEnv("SolutionLog.log");
            _env.Set(GRB.DoubleParam.MIPGap, 0.0);
            _env.Set(GRB.DoubleParam.TimeLimit, 500);
            _grbModel = new GRBModel(_env);
            //决策变量
            foreach (Node n in Data.NodeSet)
            {
                n.Result_IsServerLoacationSelected = _grbModel.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "x_" + n.ID);
            }
            _grbModel.Update();

            //目标函数
            GRBLinExpr expr1 = 0;

            foreach (Node n in Data.NodeSet)
            {
                expr1 += n.Result_IsServerLoacationSelected * (Data.ServerInstalationFee - multiplier_p[n] * Data.M);
            }
            _grbModel.SetObjective(expr1, GRB.MINIMIZE);
        }
Esempio n. 4
0
        private GRBVar[,] _x; // starting time of job j on machine a

        #endregion Fields

        #region Constructors

        public GurobiJspModel(ProblemInstance prob, string name, int tmlim_min)
        {
            _n = prob.NumJobs;
            _m = prob.NumMachines;
            Info = "Starting gurobi optimisation";
            _fileName = String.Format("jssp.{0}.log", name);

            // Model
            try
            {
                _env = new GRBEnv(_fileName);
                if (tmlim_min > 0)
                    _env.Set(GRB.DoubleParam.TimeLimit, tmlim_min*60); // time limit is set to seconds!
                _env.Set(GRB.IntParam.LogToConsole, 0);

                _model = new GRBModel(_env);
                _model.Set(GRB.StringAttr.ModelName, "jsp");
            }
            catch (GRBException e)
            {
                Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
            }

            DecisionVariables();
            ProcessingOrder(prob.Procs, prob.Sigma);
            DisjunctiveCondition(prob.Procs);
            Objective(prob.Procs, prob.Sigma);

            //TrueOptimumDecVars = Optimise(out TrueOptimum);
            //if (TrueOptimum > 0) // Objective cutoff
            //    _model.GetEnv().Set(GRB.DoubleParam.Cutoff, TrueOptimum + 0.5);
            /* Indicates that you aren't interested in solutions whose objective values
                 * are worse than the specified value. If the objective value for the optimal
                 * solution is better than the specified cutoff, the solver will return the
                 * optimal solution. Otherwise, it will terminate with a CUTOFF status.
                 */
            // seems to be only for LP relaxation, not MIP objective
        }
Esempio n. 5
0
        public void mip1_cs()
        {
            try
            {
                // Create an empty environment, set options and start
                GRBEnv env = new GRBEnv(true);
                env.Set("LogFile", "mip1.log");
                env.Start();

                // Create empty model
                GRBModel model = new GRBModel(env);

                // Create variables
                GRBVar x = model.AddVar(0.0, 1.0, 1.0, GRB.BINARY, "x");
                GRBVar y = model.AddVar(0.0, 1.0, 1.0, GRB.BINARY, "y");
                GRBVar z = model.AddVar(0.0, 1.0, 1.0, GRB.BINARY, "z");

                // Set objective: maximize x + y + 2 z
                model.SetObjective(x + y + 2 * z, GRB.MAXIMIZE);

                // Add constraint: x + 2 y + 3 z <= 4
                model.AddConstr(x + 2 * y + 3 * z <= 4.0, "c0");

                // Add constraint: x + y >= 1
                model.AddConstr(x + y >= 1.0, "c1");

                // Optimize model
                model.Optimize();

                Console.WriteLine(x.VarName + " " + x.X);
                Console.WriteLine(y.VarName + " " + y.X);
                Console.WriteLine(z.VarName + " " + z.X);

                Console.WriteLine("Obj: " + model.ObjVal);

                // Dispose of model and env
                model.Dispose();
                env.Dispose();
            }
            catch (GRBException e)
            {
                Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
            }
        }
Esempio n. 6
0
        public void ILP_Janssen()
        {
            try
            {
                // Number of plants and warehouses
                int nJobs     = 750;
                int nMachines = 12;

                double[,] processingTime =
                    new double[nMachines, nJobs];

                for (int i = 0; i < nMachines; ++i)
                {
                    for (int j = 0; j < nJobs; ++j)
                    {
                        processingTime[i, j] = new Random().Next(1000, 1500);
                    }
                }

                // Number of plants and warehouses
                //int nJobs = 20;
                //int nMachines = 3;

                //double[,] processingTime =
                //    new double[nMachines, nJobs];

                //Random Rand = new Random(1);

                //for (int i = 0; i < nMachines; ++i)
                //{
                //    if (i==0)
                //    {
                //        for (int j = 0; j < nJobs; ++j)
                //        {
                //            processingTime[i, j] = Rand.Next(100, 201);
                //        }
                //    }
                //    else if (i==1)
                //    {
                //        for (int j = 0; j < nJobs; ++j)
                //        {
                //            processingTime[i, j] = Rand.Next(100, 201);
                //        }
                //    }
                //    else if (i == 2)
                //    {
                //        for (int j = 0; j < nJobs; ++j)
                //        {
                //            processingTime[i, j] = Rand.Next(100, 201);
                //        }
                //    }
                //}

                // Create an empty environment, set options and start
                GRBEnv env = new GRBEnv(true);
                env.Set("LogFile", "mip1.log");
                env.Start();

                // Create empty model
                GRBModel model = new GRBModel(env);

                // Decision Variable
                GRBVar[,,] x = new GRBVar[nMachines, nJobs, nJobs];

                for (int i = 0; i < nMachines; ++i)
                {
                    for (int j = 0; j < nJobs; ++j)
                    {
                        for (int k = 0; k < nJobs; ++k)
                        {
                            x[i, j, k] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "x");
                        }
                    }
                }

                //Set objective:
                GRBLinExpr exp1 = 0.0;

                for (int i = 0; i < nMachines; ++i)
                {
                    for (int j = 0; j < nJobs; ++j)
                    {
                        for (int k = 0; k < nJobs; ++k)
                        {
                            GRBLinExpr exp2 = (k + 1) * processingTime[i, j] * x[i, j, k];
                            exp1.Add(exp2);
                        }
                    }
                }

                model.SetObjective(exp1, GRB.MINIMIZE);

                // Constraints
                for (int j = 0; j < nJobs; ++j)
                {
                    GRBLinExpr exp3 = 0.0;

                    for (int i = 0; i < nMachines; ++i)
                    {
                        for (int k = 0; k < nJobs; ++k)
                        {
                            exp3.AddTerm(1.0, x[i, j, k]);
                        }
                    }
                    model.AddConstr(exp3 == 1.0, "c1");
                }

                for (int i = 0; i < nMachines; ++i)
                {
                    for (int k = 0; k < nJobs; ++k)
                    {
                        GRBLinExpr exp4 = 0.0;
                        for (int j = 0; j < nJobs; ++j)
                        {
                            exp4.AddTerm(1.0, x[i, j, k]);
                        }
                        model.AddConstr(exp4 <= 1.0, "c2");
                    }
                }

                // Solve
                model.Optimize();

                for (int i = 0; i < nMachines; ++i)
                {
                    double totalMachineTime = 0.0;
                    for (int j = 0; j < nJobs; ++j)
                    {
                        for (int k = 0; k < nJobs; ++k)
                        {
                            if (x[i, j, k].X == 1.0)
                            {
                                totalMachineTime += processingTime[i, j];
                                Console.WriteLine($"({i},{j},{k})" + "ProcessingTime: " + processingTime[i, j]);
                            }
                        }
                    }
                    Console.WriteLine(totalMachineTime);
                }

                Console.WriteLine("Obj: " + model.ObjVal);

                // Dispose of model and env
                model.Dispose();
                env.Dispose();
            }
            catch (GRBException e)
            {
                Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
            }
        }
        public MasterModelOutputs buildModel(
            InputData inputData,
            int nCUT,
            MasterModelParameters masterModelParameters,
            string outputFolder
            )
        {
            try
            {
                // Create an empty environment, set options and start
                GRBEnv env = new GRBEnv(true);
                env.Set("LogFile", "LogFileGurobiModel.log");
                env.Start();

                int            T      = 4;
                List <T_param> Tlist2 = inputData.TList.FindAll(tl => tl.T >= 2);
                List <T_param> Tlist3 = inputData.TList.FindAll(tl => tl.T >= 3);

                // Create empty Gurobi model
                GRBModel gModel = new GRBModel(env);

                // Create an empty linear expression object.
                //Parameters and variables will be added and then used to add model components
                //(Objective Function and Constraints).
                GRBLinExpr expr1 = 0.0;
                GRBLinExpr expr2 = 0.0;

                // Crear la variables
                GRBVar[] Make1 = gModel.AddVars(inputData.ProdList.Count, GRB.CONTINUOUS);
                GRBVar[] Inv1  = gModel.AddVars(inputData.ProdList.Count, GRB.CONTINUOUS);
                GRBVar[] Sell1 = gModel.AddVars(inputData.ProdList.Count, GRB.CONTINUOUS);

                //MAKE1 VARNAME
                inputData.ProdList.ForEach(pl => {
                    int ixP            = inputData.ProdList.IndexOf(pl);
                    Make1[ixP].VarName = "MAKE1_Prod: " + pl.PROD;
                });

                //INV1 VARNAME
                inputData.ProdList.ForEach(pl => {
                    int ixP           = inputData.ProdList.IndexOf(pl);
                    Inv1[ixP].VarName = "INV1_Prod: " + pl.PROD;
                });

                //MAKE1 VARNAME
                inputData.ProdList.ForEach(pl => {
                    int ixP            = inputData.ProdList.IndexOf(pl);
                    Sell1[ixP].VarName = "SELL1_Prod: " + pl.PROD;
                });

                //MAKE1 => 0 (constrain)
                //GRB.CONTINUOUS
                for (int a = 0; a < Make1.Length; a++)
                {
                    expr1.Clear();
                    expr1.AddTerm(1, Make1[a]);
                    gModel.AddConstr(expr1, GRB.GREATER_EQUAL, 0, ">=0");
                }

                //INV1 => 0 (constrain)
                for (int a = 0; a < Inv1.Length; a++)
                {
                    expr1.Clear();
                    expr1.AddTerm(1, Inv1[a]);
                    gModel.AddConstr(expr1, GRB.GREATER_EQUAL, 0, ">=0");
                }

                //SELL1 => 0 (constrain)
                for (int a = 0; a < Sell1.Length; a++)
                {
                    expr1.Clear();
                    expr1.AddTerm(1, Sell1[a]);
                    gModel.AddConstr(expr1, GRB.GREATER_EQUAL, 0, ">=0");
                }

                //SELL1 <= market[p,1]
                expr1.Clear();
                inputData.ProdList.ForEach(pl => {
                    double market = inputData
                                    .MarketList
                                    .Find(ml =>
                                          ml.PROD.Equals(pl.PROD) &&
                                          ml.T.Equals(1)).MARKET;

                    int ixP = inputData.ProdList.IndexOf(pl);

                    expr1.Clear();
                    expr1.AddTerm(1, Sell1[ixP]);
                    gModel.AddConstr(expr1, GRB.LESS_EQUAL, market, "<=Market");
                });

                GRBVar Min_Stage2_Profit = gModel.AddVar(double.MinValue, double.MaxValue, masterModelParameters.Min_Stage2_Profit, GRB.CONTINUOUS, "Min_Stage2_Profit");

                //Funcion objetivo
                //maximize Expected_Profit:
                //sum { s in SCEN}
                //prob[s] *
                //sum { p in PROD} (revenue[p, 1, s] * Sell1[p] -
                //prodcost[p] * Make1[p] - invcost[p] * Inv1[p]) +
                //Min_Stage2_Profit;
                expr1.Clear();
                inputData.ScenList.ForEach(sl =>
                {
                    double prob = inputData.ProbList.Find(x => x.SCEN.Equals(sl.SCEN)).PROB;

                    inputData.ProdList.ForEach(pl => {
                        double revenue = inputData
                                         .RevenueList
                                         .Find(rl =>
                                               rl.PROD.Equals(pl.PROD) &&
                                               rl.T.Equals(1) &&
                                               rl.SCEN.Equals(sl.SCEN))
                                         .REVENUE;

                        int ixP        = inputData.ProdList.IndexOf(pl);
                        double invcost = inputData.InvCostList.Find(inv => inv.PROD.Equals(pl.PROD)).INVCOST;

                        double prodcost = inputData.ProdCostList.Find(inv => inv.PROD.Equals(pl.PROD)).PRODCOST;

                        expr1.AddTerm(prob * revenue, Sell1[ixP]);
                        expr1.AddTerm(-prob * prodcost, Make1[ixP]);
                        expr1.AddTerm(-prob * invcost, Inv1[ixP]);
                        expr1.AddTerm(1, Min_Stage2_Profit);
                    });
                });

                //Insertar funcion objetivo
                gModel.SetObjective(expr1, GRB.MAXIMIZE);

                //subj to Cut_Defn {k in 1..nCUT}:  KE ESTA PASANDO
                // Min_Stage2_Profit <=
                //    sum { t in 2..T, s in SCEN}
                //    time_price[t, s, k] * avail[t] +
                //    sum { p in PROD, s in SCEN}
                //    bal2_price[p, s, k] * (-Inv1[p]) +
                //    sum { p in PROD, t in 2..T, s in SCEN}
                //    sell_lim_price[p, t, s, k] * market[p, t];
                expr1.Clear();
                double sumPriceAvail  = 0;
                double sumPriceMarket = 0;
                expr1.AddTerm(1, Min_Stage2_Profit);
                for (int k = 1; k <= nCUT; k++)
                {
                    //    sum { t in 2..T, s in SCEN}
                    //    time_price[t, s, k] * avail[t] +
                    Tlist2.ForEach(tl =>
                    {
                        inputData.ScenList.ForEach(sl =>
                        {
                            double timePrice = masterModelParameters.timePriceList.Find(x => x.T.Equals(tl.T) &&
                                                                                        x.SCEN.Equals(sl.SCEN) &&
                                                                                        x.nCUT.Equals(k)).TIMEPRICE;

                            double avail = inputData.AvailList.Find(al => al.T.Equals(tl.T)).AVAIL;

                            sumPriceAvail += timePrice * avail;
                        });
                    });

                    //    sum { p in PROD, t in 2..T, s in SCEN}
                    //    sell_lim_price[p, t, s, k] * market[p, t];
                    inputData.ProdList.ForEach(pl => {
                        Tlist2.ForEach(tl => {
                            inputData.ScenList.ForEach(sl =>
                            {
                                double sellLimPrice = masterModelParameters.sellLimPriceList.Find(slpl => slpl.PROD.Equals(pl.PROD) &&
                                                                                                  slpl.T.Equals(tl.T) &&
                                                                                                  slpl.SCEN.Equals(sl.SCEN) &&
                                                                                                  slpl.nCUT.Equals(k)).SELLLIMPRICE;

                                double market = inputData.MarketList.Find(ml => ml.PROD.Equals(pl.PROD) &&
                                                                          ml.T.Equals(tl.T)).MARKET;

                                sumPriceMarket += sellLimPrice * market;
                            });
                        });
                    });

                    //  sum { p in PROD, s in SCEN}
                    //  bal2_price[p, s, k] * (-Inv1[p]) +
                    inputData.ProdList.ForEach(pl =>
                    {
                        inputData.ScenList.ForEach(sl =>
                        {
                            double bal2Price = masterModelParameters.balance2PriceList.Find(bp => bp.PROD.Equals(pl.PROD) &&
                                                                                            bp.SCEN.Equals(sl.SCEN) && bp.nCUT.Equals(k)).BALANCE2PRICE;

                            int ixP = inputData.ProdList.IndexOf(pl);
                            expr1.AddTerm(bal2Price, Inv1[ixP]);
                        });
                    });
                }
                gModel.AddConstr(expr1, GRB.LESS_EQUAL, sumPriceAvail + sumPriceMarket, "Cut_Defn");

                //subject to Time1:
                //sum { p in PROD} (1 / rate[p]) * Make1[p] <= avail[1];
                expr1.Clear();
                inputData.ProdList.ForEach(pl =>
                {
                    double rate      = 1 / inputData.RateList.Find(rl => rl.PROD.Equals(pl.PROD)).RATE;
                    double avail     = inputData.AvailList.Find(al => al.T.Equals(1)).AVAIL;
                    double rateAvail = avail / rate;
                    int ixP          = inputData.ProdList.IndexOf(pl);

                    gModel.AddConstr(Make1[ixP], GRB.GREATER_EQUAL, rateAvail, "TIME1");
                });


                //subject to Balance1 { p in PROD}:
                //Make1[p] + inv0[p] = Sell1[p] + Inv1[p];
                expr1.Clear();
                inputData.ProdList.ForEach(pl => {
                    expr1.Clear();

                    int ixP = inputData.ProdList.IndexOf(pl);
                    expr1.AddTerm(1, Sell1[ixP]);
                    expr1.AddTerm(1, Inv1[ixP]);
                    expr1.AddTerm(-1, Make1[ixP]);

                    double inv0 = inputData.Inv0List.Find(il => il.PROD.Equals(pl.PROD)).INV0;

                    gModel.AddConstr(inv0, GRB.EQUAL, expr1, "Balance1");
                });

                //carpeta donde se expande el modelo master
                gModel.Write(outputFolder + "Master_Model.lp");

                // RESOLVER EL MODELO
                try
                {
                    Console.WriteLine("Solving the master model with gurobi..");
                    gModel.Optimize();

                    if (gModel.Status == 2)
                    {
                        for (int m = 0; m < Make1.Length; m++)
                        {
                            Console.WriteLine("Make1 Var: " + Make1[m].VarName + " = " + Make1[m].X);
                        }

                        for (int m = 0; m < Inv1.Length; m++)
                        {
                            Console.WriteLine("Inv1 Var: " + Inv1[m].VarName + " = " + Inv1[m].X);
                        }

                        for (int m = 0; m < Sell1.Length; m++)
                        {
                            Console.WriteLine("Sell Var: " + Sell1[m].VarName + " = " + Sell1[m].X);
                        }
                    }

                    gModel.Dispose();
                    env.Dispose();

                    return(new MasterModelOutputs()
                    {
                        make1 = Make1,
                        sell1 = Sell1,
                        inv1 = Inv1,
                        expectedProfit = gModel.ObjVal,
                        gModel = gModel
                    });
                }
                catch { Console.WriteLine("ERROR SOLVING THE MODEL"); }

                gModel.Dispose();
                env.Dispose();
            }
            catch (GRBException ex)
            {
                Console.WriteLine("Error code: " + ex.ErrorCode + ". " + ex.Message);
            }

            return(new MasterModelOutputs());
        }
Esempio n. 8
0
        public Dictionary <string, string> Solve(bool tune, bool debug, string tuneOutputFile = null, string paramFile = null)
        {
            var times = new Dictionary <string, string>();

            var totalTime = Utils.TimeAction(() =>
            {
                try
                {
                    // Create an empty environment, set options and start
                    GRBEnv env = new GRBEnv(true);

                    // Use gurobi's parameter reader to load the best file
                    if (paramFile != null)
                    {
                        env.ReadParams(paramFile);
                    }

                    if (!debug)
                    {
                        env.Set(GRB.IntParam.OutputFlag, 0);
                    }

                    env.Start();

                    // Create empty model
                    GRBModel model = new GRBModel(env);

                    (var grbSeated, var addDecisionVariableTime) = Utils.TimeFunction(() => AddSeatedBinaryVariables(model), "Add Decision Variables");

                    var addConstraintsTime = Utils.TimeAction(() => AddContraints(model, grbSeated), "Add Constraints");

                    var addObjectiveTime = Utils.TimeAction(() => AddObjective(model, grbSeated), "Add Objective");

                    var optimizeTime = Utils.TimeAction(() => model.Optimize(), "Optimizing");

                    if (tune)
                    {
                        model.Tune();

                        model.GetTuneResult(0);
                        model.Write(tuneOutputFile);
                    }

                    SeatGroups(grbSeated);

                    // Dispose of model and env
                    model.Dispose();
                    env.Dispose();

                    times.Add("Add Decision Variables", addDecisionVariableTime);
                    times.Add("Add Constraints", addConstraintsTime);
                    times.Add("Add Objective", addObjectiveTime);
                    times.Add("Optimizing", optimizeTime);
                }
                catch (GRBException e)
                {
                    Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
                    throw e;
                }
            }, "Total");

            times.Add("Total", totalTime);

            return(times);
        }
        public override Schedule Generate(Scenario scenario, FilledBaseline baseline = null)
        {
            var opportunities    = scenario.Projects.Where(p => p is Opportunity).ToArray();
            var batches          = scenario.Projects.SelectMany(p => p.Batches).ToArray();
            var batchesBothLines = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Both).ToArray();
            var batchesLine1     = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Line1).ToArray();
            var batchesLine2     = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Line2).ToArray();

            int       bBc          = batchesBothLines.Count();
            var       earliestDate = scenario.Projects.Min(p => p.DeliveryDate);
            var       lastDate     = scenario.Projects.Max(p => p.DeliveryDate.AddDays(7 * p.Batches.Count())).AddDays(7 * 3); // add an additional buffer of 3 weeks.
            double    maxDays      = (lastDate - earliestDate).Days;
            var       maxWeek      = maxDays / 7 + 2;
            const int weeksBuffer  = 3;

            const double numericScale = 100d;

            const double delayPenaltyMultiplier    = 0.01;            // 1% of revenue per week
            const double interestPenaltyMultiplier = (0.045 / 52.14); // 4.5%  / 52.14 * (revenue – margin)


            var env = new GRBEnv();

            env.Set(GRB.IntParam.UpdateMode, 1);
            env.Set(GRB.IntParam.LogToConsole, 1);
            //env.Set(GRB.IntParam.ScaleFlag, 2);
            //env.Set(GRB.IntParam.Presolve, 0);
            //env.Set(GRB.DoubleParam.TimeLimit, _config.TimeLimit.Value);
            //env.Set(GRB.DoubleParam.MIPGap, 0.02);

            var m = new GRBModel(env);

            // decide: which batch is when, on which line
            // [batch, time]
            var varBatchTimeLine1 = m.AddVars(bBc + batchesLine1.Count(), bBc + batchesLine1.Count(), 0, 1, GRB.BINARY, "varBatchTimeLine1");
            var varBatchTimeLine2 = m.AddVars(bBc + batchesLine2.Count(), bBc + batchesLine2.Count(), 0, 1, GRB.BINARY, "varBatchTimeLine2");
            // what are the time differences between times
            var varBatchTimeDiffsLine1 = m.AddVars(bBc + batchesLine1.Count() - 1, 0, maxDays, GRB.CONTINUOUS, "varBatchTimeDiffsLine1");
            var varBatchTimeDiffsLine2 = m.AddVars(bBc + batchesLine2.Count() - 1, 0, maxDays, GRB.CONTINUOUS, "varBatchTimeDiffsLine2");
            var varLineDecision        = m.AddVars(bBc, 0, 1, GRB.BINARY, "varLineDecision"); // 0 = Line1, 1 = Line2


            // make stupid solution:
            // assign fixed projects, set the rest as unused
            m.Update();
            for (int l = 0; l < 2; l++)
            {
                var line  = l == 0 ? varBatchTimeLine1 : varBatchTimeLine2;
                var diffs = l == 0 ? varBatchTimeDiffsLine1 : varBatchTimeDiffsLine2;
                for (int i = 0; i < diffs.GetLength(0); i++)
                {
                    diffs[i].Set(GRB.DoubleAttr.Start, 0);
                }
                //m.AddConstr(diffs[i] == 0, "initial solution diffs");

                int lineT = 0;
                for (int bi = bBc; bi < line.GetLength(0); bi++)
                {
                    var  batch          = (l == 0 ? batchesLine1 : batchesLine2)[bi - bBc];
                    bool isFixedProject = scenario.Projects.First(p => p.Batches.Contains(batch)) is FixedProject;
                    if (isFixedProject)
                    {
                        line[bi, lineT].Set(GRB.DoubleAttr.Start, 1);
                        //m.AddConstr(line[bi, lineT] == 1, "assign batches of project in succession");
                        for (int j = 0; j < line.GetLength(1); j++)
                        {
                            if (j != lineT)
                            {
                                line[bi, j].Set(GRB.DoubleAttr.Start, 0);
                            }
                        }
                        //m.AddConstr(line[bi, j] == 0, "initial solution");
                        lineT++;
                    }
                    else
                    {
                        for (int j = 0; j < line.GetLength(1); j++)
                        {
                            line[bi, j].Set(GRB.DoubleAttr.Start, 0);
                        }
                        //m.AddConstr(line[bi, j] == 0, "initial solution");
                    }
                }

                // make zeros also for both line batches
                for (int i = 0; i < bBc; i++)
                {
                    for (int j = 0; j < line.GetLength(1); j++)
                    {
                        line[i, j].Set(GRB.DoubleAttr.Start, 0);
                    }
                }
                //m.AddConstr(line[i, j] == 0, "initial solution");
            }


            // line constraints:

            // assign batch only once (constraint for single lines then both lines)
            // if it's a fixed project though, then it must be allocated
            for (int l = 0; l < 2; l++)
            {
                var line = l == 0 ? varBatchTimeLine1 : varBatchTimeLine2;
                for (int bi = bBc; bi < line.GetLength(0); bi++)
                {
                    var batchTotal = new GRBLinExpr();
                    for (int t = 0; t < line.GetLength(1); t++)
                    {
                        batchTotal += line[bi, t];
                    }
                    var  batch          = (l == 0 ? batchesLine1 : batchesLine2)[bi - bBc];
                    bool isFixedProject = scenario.Projects.First(p => p.Batches.Contains(batch)) is FixedProject;
                    if (!isFixedProject)
                    {
                        m.AddConstr(batchTotal <= 1, "assign batch only once");
                    }
                    else
                    {
                        m.AddConstr(batchTotal == 1, "assign batch exactly once");
                    }
                }
            }
            for (int bi = 0; bi < bBc; bi++)
            {
                var batchTotal = new GRBLinExpr();
                for (int t = 0; t < varBatchTimeLine1.GetLength(1); t++)
                {
                    batchTotal += varBatchTimeLine1[bi, t];
                }
                for (int t = 0; t < varBatchTimeLine2.GetLength(1); t++)
                {
                    batchTotal += varBatchTimeLine2[bi, t];
                }

                var  batch          = batchesBothLines[bi];
                bool isFixedProject = scenario.Projects.First(p => p.Batches.Contains(batch)) is FixedProject;
                if (!isFixedProject)
                {
                    m.AddConstr(batchTotal <= 1, "assign batch only once");
                }
                else
                {
                    m.AddConstr(batchTotal == 1, "assign batch exactly once");
                }
            }

            // a time slot can only be used once on each line
            for (int l = 0; l < 2; l++)
            {
                var line = l == 0 ? varBatchTimeLine1 : varBatchTimeLine2;
                for (int t = 0; t < line.GetLength(1); t++)
                {
                    var timeTotal = new GRBLinExpr();
                    for (int bi = 0; bi < line.GetLength(0); bi++)
                    {
                        timeTotal += line[bi, t];
                    }
                    m.AddConstr(timeTotal <= 1, "assign time slot only once on line " + (l + 1).ToString());
                }
            }


            // for all batches which aren't assigned to a line yet, limit the allocation of yet unassigned batches of a project to one line
            // TODO: If project has e.g. line1 and both lines, limit both lines to line 1?
            for (int pi = 0; pi < scenario.Projects.Count(); pi++)
            {
                var p = scenario.Projects[pi];

                if (!p.Batches.Any(b => b.Compatibility == Batch.LineCompatibility.Both))
                {
                    continue;
                }

                int lineRestriction = -1;
                if (p.Batches.Any(b => b.Compatibility != Batch.LineCompatibility.Both))
                {
                    lineRestriction = p.Batches.First(b => b.Compatibility != Batch.LineCompatibility.Both).Compatibility == Batch.LineCompatibility.Line1 ? 0 : 1;
                }

                int i_prev = -1;
                for (int j = 0; j < p.Batches.Count(); j++)
                {
                    if (p.Batches[j].Compatibility != Batch.LineCompatibility.Both)
                    {
                        continue;
                    }

                    var i = batchesBothLines.IndexOf(p.Batches[j]);
                    if (lineRestriction == -1)
                    {
                        if (i_prev != -1)
                        {
                            m.AddConstr(varLineDecision[i] == varLineDecision[i_prev], "lineDecisionRestrictionAllSame");
                        }
                    }
                    else
                    {
                        // if there are other batches on this project which are already on a specific line, limit to the same line
                        m.AddConstr(varLineDecision[i] == lineRestriction, "lineDecisionRestrictionSpecific");
                    }
                    i_prev = i;
                }
            }


            // for each project, either all or no batches must be assigned
            var totalBatchesOfProject = new Dictionary <Project, List <GRBLinExpr> >();

            foreach (var p in scenario.Projects)
            {
                var allBatches = new List <GRBLinExpr>();

                // gather the total of all batches
                GRBLinExpr previousBatchTotal = null;
                for (int bi = 0; bi < p.Batches.Count(); bi++)
                {
                    var b          = p.Batches[bi];
                    var batchTotal = new GRBLinExpr();
                    if (b.Compatibility == Batch.LineCompatibility.Line1)
                    {
                        var bIndex = bBc + batchesLine1.IndexOf(b);
                        for (int ti = 0; ti < varBatchTimeLine1.GetLength(1); ti++)
                        {
                            batchTotal += varBatchTimeLine1[bIndex, ti];
                        }
                    }
                    else if (b.Compatibility == Batch.LineCompatibility.Line2)
                    {
                        var bIndex = bBc + batchesLine2.IndexOf(b);
                        for (int ti = 0; ti < varBatchTimeLine2.GetLength(1); ti++)
                        {
                            batchTotal += varBatchTimeLine2[bIndex, ti];
                        }
                    }
                    else
                    {
                        var bIndex = batchesBothLines.IndexOf(b);
                        for (int t = 0; t < varBatchTimeLine1.GetLength(1); t++)
                        {
                            batchTotal += varBatchTimeLine1[bIndex, t];
                        }
                        for (int t = 0; t < varBatchTimeLine2.GetLength(1); t++)
                        {
                            batchTotal += varBatchTimeLine2[bIndex, t];
                        }
                    }

                    // the sum of this batch over all times (0 or 1) has to be the same as the sum of the previous one
                    if (bi > 0)
                    {
                        //m.AddConstr(previousBatchTotal == batchTotal, "allBatchesAllocatedOrNot");
                    }

                    previousBatchTotal = batchTotal;
                    allBatches.Add(batchTotal);
                }
                totalBatchesOfProject.Add(p, allBatches);
            }

            // Tbd: Only half of the batch slots of line 1 may be occupied. Sometimes existst as internal projects.
            // fill gap between 50% and internal projects, monthly resolution

            // gather the time of time slots
            GRBLinExpr[] startTimesLine1    = new GRBLinExpr[varBatchTimeLine1.GetLength(1)];
            GRBLinExpr[] startTimesLine2    = new GRBLinExpr[varBatchTimeLine2.GetLength(1)];
            var          varStartTimesLine1 = m.AddVars(startTimesLine1.Count(), 0, maxDays, GRB.CONTINUOUS, "varStartTimesLine1");
            var          varStartTimesLine2 = m.AddVars(startTimesLine2.Count(), 0, maxDays, GRB.CONTINUOUS, "varStartTimesLine2");

            for (int l = 0; l < 2; l++)
            {
                var batchLine      = l == 0 ? varBatchTimeLine1 : varBatchTimeLine2;
                var startTimes     = l == 0 ? startTimesLine1 : startTimesLine2;
                var batchesLine    = l == 0 ? batchesLine1 : batchesLine2;
                var batchTimeDiffs = l == 0 ? varBatchTimeDiffsLine1 : varBatchTimeDiffsLine2;

                for (int t = 0; t < batchLine.GetLength(1); t++)
                {
                    startTimes[t] = new GRBLinExpr();
                    // sum up all durations and buffer times before this time instance
                    for (int previous_t = 0; previous_t < t; previous_t++)
                    {
                        var duration_prev_t = new GRBLinExpr();
                        for (int bi = 0; bi < batchLine.GetLength(0); bi++)
                        {
                            var batch    = bi < bBc ? batchesBothLines[bi] : batchesLine[bi];
                            var duration = batch.UsedWorkHours / 24d;
                            duration_prev_t += batchLine[bi, previous_t] * duration;
                        }
                        duration_prev_t += batchTimeDiffs[previous_t];

                        startTimes[t] += duration_prev_t;
                    }
                }

                var varStartTime = l == 0 ? varStartTimesLine1 : varStartTimesLine2;
                for (int i = 0; i < varStartTime.Count(); i++)
                {
                    m.AddConstr(varStartTime[i] == startTimes[i], "varStartTimeAssignment");
                }
            }


            // gather the start time of the batches
            var varStartTimesBatches1 = m.AddVars(varBatchTimeLine1.GetLength(0), 0, maxDays, GRB.CONTINUOUS, "varBatchStartTimesLine1");
            var varStartTimesBatches2 = m.AddVars(varBatchTimeLine2.GetLength(0), 0, maxDays, GRB.CONTINUOUS, "varBatchStartTimesLine2");

            for (int l = 0; l < 2; l++)
            {
                var batchLine       = l == 0 ? varBatchTimeLine1 : varBatchTimeLine2;
                var batchStartTimes = l == 0 ? varStartTimesBatches1 : varStartTimesBatches2;
                var batchesLine     = l == 0 ? batchesLine1 : batchesLine2;
                var varStartTimes   = l == 0 ? varStartTimesLine1 : varStartTimesLine2;

                for (int bi = 0; bi < batchLine.GetLength(0); bi++)
                {
                    // make the batch take the value of its time slot
                    for (int ti = 0; ti < batchLine.GetLength(1); ti++)
                    {
                        m.AddConstr(batchStartTimes[bi] <= varStartTimes[ti] + maxDays * (1 - batchLine[bi, ti]), "batchTimeAssignment");
                        m.AddConstr(batchStartTimes[bi] >= varStartTimes[ti] - maxDays * (1 - batchLine[bi, ti]), "batchTimeAssignment");
                    }
                }
            }


            // scheduling formulation
            for (int l = 0; l < 2; l++)
            {
                var varBatchStartTimes = l == 0 ? varStartTimesBatches1 : varStartTimesBatches2;
                var line = batchesBothLines.ToList();
                line.AddRange(l == 0 ? batchesLine1 : batchesLine2);

                for (int bi1 = 0; bi1 < line.Count() - 1; bi1++)
                {
                    var batch1         = line[bi1];
                    var o1_exists      = opportunities.FirstOrDefault(o => o.Batches.Contains(batch1));
                    var oi1            = o1_exists != null ? (1 - totalBatchesOfProject[o1_exists][0]) : new GRBLinExpr();
                    var s1             = varBatchStartTimes[bi1];
                    var bothLineSlack1 = (bi1 < bBc) ? (l == 0 ? varLineDecision[bi1] - 0 : 1 - varLineDecision[bi1]) : new GRBLinExpr();
                    for (int bi2 = bi1 + 1; bi2 < line.Count(); bi2++)
                    {
                        var batch2         = line[bi2];
                        var o2_exists      = opportunities.FirstOrDefault(o => o.Batches.Contains(batch2));
                        var oi2            = o2_exists != null ? (1 - totalBatchesOfProject[o1_exists][0]) : new GRBLinExpr();
                        var s2             = varBatchStartTimes[bi2];
                        var bothLineSlack2 = (bi2 < bBc) ? (l == 0 ? varLineDecision[bi2] - l : l - varLineDecision[bi2]) : new GRBLinExpr();

                        // S1 - E2 >= 0 OR S2 - E1 >= 0
                        // IF both batches are used
                        var decisionVar                   = m.AddVar(0, 1, GRB.BINARY, "schedulingORvar");
                        var opportunityNotUsedSlack       = oi1 + oi2;
                        var varBothLinesLineDecisionSlack = bothLineSlack1 + bothLineSlack2; // LineDecisionSlack for both lines batches, i.e. if a bothline batch is used, don't restrict unused line.
                        m.AddConstr(s1 - (s2 + batch2.UsedWorkHours / 24d) >= -maxDays * (decisionVar + opportunityNotUsedSlack + varBothLinesLineDecisionSlack), "batchesScheduling");
                        m.AddConstr(s2 - (s1 + batch1.UsedWorkHours / 24d) >= -maxDays * (1 - decisionVar + opportunityNotUsedSlack + varBothLinesLineDecisionSlack), "batchesScheduling");
                    }
                }
            }


            // Maximize the margin (including delay and interest penalties) and the workload
            // Only the delivery of the first batch is really important. The rest is less important.

            var margin     = new GRBLinExpr();
            var delays     = new List <GRBLinExpr>();
            var interests  = new List <GRBLinExpr>();
            var weekValues = new List <GRBLinExpr>();
            var weekDiffs  = new List <GRBLinExpr>();

            foreach (var p in scenario.Projects)
            {
                // if the project is used add the margin
                if (p is Opportunity)
                {
                    margin += totalBatchesOfProject[p][0] * p.Margin * (1d / numericScale);
                }
                else
                {
                    margin += p.Margin / numericScale;
                }

                // deduct the delay penalty for each batch.
                var startDayOfProject = (p.DeliveryDate - earliestDate).TotalDays;
                var varMaxedValue     = m.AddVars(p.Batches.Count(), 0, maxWeek, GRB.CONTINUOUS, "penaltyIndicator" + p.Description);
                //var varDecisionVar = m.AddVars(p.Batches.Count(), 0, 1, GRB.BINARY, "penaltyIndicator" + p.Description);

                //m.Update();
                GRBLinExpr previousWeekValue = new GRBLinExpr();
                for (int pbi = 0; pbi < p.Batches.Count(); pbi++)
                {
                    var b = p.Batches[pbi];
                    // compare the minimal batch time (3 + 1 weeks) to the actual delivery time

                    GRBLinExpr weekValue;
                    if (batchesLine1.Contains(b))
                    {
                        var bi = batchesLine1.IndexOf(b) + bBc;
                        weekValue = varStartTimesBatches1[bi] / 7d;
                    }
                    else if (batchesLine2.Contains(b))
                    {
                        var bi = batchesLine2.IndexOf(b) + bBc;
                        weekValue = varStartTimesBatches2[bi] / 7d;
                    }
                    else
                    {
                        var bi = batchesBothLines.IndexOf(b);
                        // create a new var
                        var bothLineWeekVar = m.AddVar(0, maxWeek, GRB.CONTINUOUS, "weekValueBothLines");
                        //m.Update();
                        m.AddConstr(bothLineWeekVar >= varStartTimesBatches1[bi] / 7d - (varLineDecision[bi]) * maxWeek, "weekValueBothLinesDefinitionByConstraints");
                        m.AddConstr(bothLineWeekVar <= varStartTimesBatches1[bi] / 7d + (varLineDecision[bi]) * maxWeek, "weekValueBothLinesDefinitionByConstraints");
                        m.AddConstr(bothLineWeekVar >= varStartTimesBatches2[bi] / 7d - (1 - varLineDecision[bi]) * maxWeek, "weekValueBothLinesDefinitionByConstraints");
                        m.AddConstr(bothLineWeekVar <= varStartTimesBatches2[bi] / 7d + (1 - varLineDecision[bi]) * maxWeek, "weekValueBothLinesDefinitionByConstraints");
                        weekValue = bothLineWeekVar;
                    }


                    if (true || pbi < p.Batches.Count() - 1)
                    {
                        // for positive difference add delay penalty, for negative difference add interest penalty
                        // x = opportunity used ? x : 0
                        // var = max(0, x)
                        // margin += var * delay
                        // margin += (x - var) * interest
                        var        plannedWeek = startDayOfProject / 7d + pbi * (weeksBuffer + 1);
                        GRBLinExpr weekDiff;
                        if (p is Opportunity)
                        {
                            var weekDiffIfUsed = m.AddVar(-maxWeek, maxWeek, GRB.CONTINUOUS, "weekDiffIfUsed");
                            //m.Update();
                            var wD = weekValue - plannedWeek;
                            m.AddConstr(weekDiffIfUsed >= wD - (1 - totalBatchesOfProject[p][0]) * maxWeek, "weekDiffConstraintDefinition1");
                            m.AddConstr(weekDiffIfUsed <= wD + (1 - totalBatchesOfProject[p][0]) * maxWeek, "weekDiffConstraintDefinition2");
                            m.AddConstr(weekDiffIfUsed <= (totalBatchesOfProject[p][0]) * maxWeek, "weekDiffConstraintDefinition3");
                            m.AddConstr(weekDiffIfUsed >= -(totalBatchesOfProject[p][0]) * maxWeek, "weekDiffConstraintDefinition4");
                            weekDiff = weekDiffIfUsed;
                        }
                        else
                        {
                            weekDiff = weekValue - plannedWeek;
                        }

                        m.AddConstr(varMaxedValue[pbi] >= weekDiff, "maxedWeekDiffValueConstraintDefinition");
                        //m.AddConstr(varMaxedValue[pbi] >= 0, "maxedWeekDiffValueConstraintDefinition");
                        //m.AddConstr(varMaxedValue[pbi] <= weekDiff + maxWeek * (varDecisionVar[pbi]), "maxedWeekDiffValueConstraintDefinition");
                        //m.AddConstr(varMaxedValue[pbi] <= 0 + maxWeek * (1 - varDecisionVar[pbi]), "maxedWeekDiffValueConstraintDefinition");


                        double firstBatchImportance = pbi == 0 ? 1 : 0.2; // mainly the first batch is important, the rest is meh
                        weekValues.Add(weekValue);
                        weekDiffs.Add(weekDiff);
                        double revenueBase = p.Revenue == 0 ? 1E+8 : p.Revenue; // if it's an internal project it must not be shifted, therefore we make the penalty high
                        revenueBase /= numericScale;
                        delays.Add(varMaxedValue[pbi] * delayPenaltyMultiplier * revenueBase * firstBatchImportance);
                        interests.Add(-(weekDiff - varMaxedValue[pbi]) * interestPenaltyMultiplier * revenueBase * firstBatchImportance);
                        margin -= delays.Last() + interests.Last();
                    }

                    // constraint: batches of a project have to be in succession, i.e. batch2 can't come after batch3 chronologically
                    if (pbi > 0)
                    {
                        m.AddConstr(weekValue - previousWeekValue >= 0, "batchesOfProjectHaveToBeInSuccession");
                    }
                    previousWeekValue = weekValue;
                }
            }



            //m.SetObjective(margin, GRB.MAXIMIZE);

            m.Update();
            m.Write("ilproman3.mps");
            m.Write("ilproman3.mst");

            //m.Tune();
            //m.GetTuneResult(0);
            m.Optimize();

            //env.Set(GRB.IntParam.IISMethod, 0); // makes IIS computation fast but potentially inaccurate
            m.ComputeIIS();
            m.Write("ilp.ilp");


            // TODO: Max 5 weeks delay per project


            // build the solution from the optimization
            var    sol                = new Schedule();
            int    batchCount         = 0;
            double cumulatedDelays    = 0;
            double cumulatedInterests = 0;

            foreach (var p in scenario.Projects)
            {
                List <Schedule.BatchAllocation> allocatedBatches = new List <Schedule.BatchAllocation>();
                var data = new List <double[]>();
                for (int bi = 0; bi < p.Batches.Count(); bi++)
                {
                    var b        = p.Batches[bi];
                    var delay    = delays[batchCount].Value;
                    var interest = interests[batchCount].Value;
                    cumulatedDelays    += delay;
                    cumulatedInterests += interest;
                    var weekValue = weekValues[batchCount].Value;
                    var weekDiff  = weekDiffs[batchCount].Value;
                    // figure out on which week and which line the batch is allocated
                    double dayLine1 = 0;
                    double dayLine2 = 0;
                    if (b.Compatibility == Batch.LineCompatibility.Line1)
                    {
                        dayLine1 = varStartTimesBatches1[bBc + batchesLine1.IndexOf(b)].Get(GRB.DoubleAttr.X);
                    }
                    else if (b.Compatibility == Batch.LineCompatibility.Line2)
                    {
                        dayLine2 = varStartTimesBatches2[bBc + batchesLine2.IndexOf(b)].Get(GRB.DoubleAttr.X);
                    }
                    else
                    {
                        var bbi          = batchesBothLines.IndexOf(b);
                        var lineDecision = varLineDecision[bbi].Get(GRB.DoubleAttr.X);
                        dayLine1 = varStartTimesBatches1[bbi].Get(GRB.DoubleAttr.X) * (1 - lineDecision);
                        dayLine2 = varStartTimesBatches2[bbi].Get(GRB.DoubleAttr.X) * lineDecision;
                    }

                    data.Add(new double[] { delay, interest, weekValue, weekDiff, dayLine1 + dayLine2 });

                    if (dayLine1 > 0 && dayLine2 > 0)
                    {
                        throw new InvalidOperationException();
                    }

                    var alloc = Schedule.LineAllocation.None;
                    if (p is FixedProject || (p is Opportunity && totalBatchesOfProject[p][0].Value > 0.5))
                    {
                        if (b.Compatibility == Batch.LineCompatibility.Both)
                        {
                            alloc = varLineDecision[batchesBothLines.IndexOf(b)].Get(GRB.DoubleAttr.X) > 0.5 ? Schedule.LineAllocation.Line2 : Schedule.LineAllocation.Line1;
                        }
                        else
                        {
                            alloc = b.Compatibility == Batch.LineCompatibility.Line1 ? Schedule.LineAllocation.Line1 : Schedule.LineAllocation.Line2;
                        }
                    }
                    //if (dayLine1 > 0) alloc = Solution.LineAllocation.Line1;
                    //if (dayLine2 > 0) alloc = Solution.LineAllocation.Line2;

                    allocatedBatches.Add(new Schedule.BatchAllocation(b, alloc, earliestDate.AddDays(dayLine1 + dayLine2)));
                    batchCount++;
                }
                sol.Add(new Schedule.ProjectAllocation(p, allocatedBatches));
            }

            /*var maxMustWinDate = mustWinOpportunitites.Max(p => p.DeliveryDate.AddDays(7 * p.Batches.Count())).AddDays(7 * 3);
             * var minMustWinDate = mustWinOpportunitites.Min(p2 => p2.DeliveryDate);
             * var sevenmonthsrange = sol.Where(p => p.Project.DeliveryDate >= minMustWinDate && p.Project.DeliveryDate <= maxMustWinDate).ToArray();
             * var ops = sevenmonthsrange.Where(p => mustWinOpportunitites.Contains(p.Project) || p.Project is FixedProject);
             * var s = ops.Sum(p => p.Project.Revenue);*/

            return(sol);
        }
Esempio n. 10
0
        public SubModelOutputs Build_Model(
            InputData inputData,
            string outputFolder
            )
        {
            try
            {
                // Create an empty environment, set options and start
                GRBEnv env = new GRBEnv(true);
                env.Set("LogFile", "LogFileGurobiModel.log");
                env.Start();

                int            T      = 4;
                List <T_param> Tlist2 = inputData.TList.FindAll(tl => tl.T >= 2);
                List <T_param> Tlist3 = inputData.TList.FindAll(tl => tl.T >= 3);

                // Create empty Gurobi model
                GRBModel gModel = new GRBModel(env);

                // Create an empty linear expression object.
                //Parameters and variables will be added and then used to add model components
                //(Objective Function and Constraints).
                GRBLinExpr expr1 = 0.0;
                GRBLinExpr expr2 = 0.0;

                // Crear la variables
                GRBVar[] Make = gModel.AddVars(inputData.ProdList.Count * Tlist2.Count * inputData.ScenList.Count, GRB.CONTINUOUS);
                GRBVar[] Inv  = gModel.AddVars(inputData.ProdList.Count * Tlist2.Count * inputData.ScenList.Count, GRB.CONTINUOUS);
                GRBVar[] Sell = gModel.AddVars(inputData.ProdList.Count * Tlist2.Count * inputData.ScenList.Count, GRB.CONTINUOUS);

                //MAKE VARNAME
                inputData.ProdList.ForEach(pl => {
                    Tlist2.ForEach(tl => {
                        inputData.ScenList.ForEach(sl =>
                        {
                            int ixP          = inputData.ProdList.IndexOf(pl);
                            int ixT          = Tlist2.IndexOf(tl);
                            int ixS          = inputData.ScenList.IndexOf(sl);
                            int ix           = ixVar.getIx3(ixP, ixT, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count);
                            Make[ix].VarName = "MAKE_PROD: " + pl.PROD + " T: " + tl.T + " SCEN: " + sl.SCEN;
                        });
                    });
                });

                //INV VARNAME
                inputData.ProdList.ForEach(pl => {
                    Tlist2.ForEach(tl => {
                        inputData.ScenList.ForEach(sl =>
                        {
                            int ixP         = inputData.ProdList.IndexOf(pl);
                            int ixT         = Tlist2.IndexOf(tl);
                            int ixS         = inputData.ScenList.IndexOf(sl);
                            int ix          = ixVar.getIx3(ixP, ixT, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count);
                            Inv[ix].VarName = "INV_PROD: " + pl.PROD + " T: " + tl.T + " SCEN: " + sl.SCEN;
                        });
                    });
                });

                //SELL VARNAME
                inputData.ProdList.ForEach(pl => {
                    Tlist2.ForEach(tl => {
                        inputData.ScenList.ForEach(sl =>
                        {
                            int ixP          = inputData.ProdList.IndexOf(pl);
                            int ixT          = Tlist2.IndexOf(tl);
                            int ixS          = inputData.ScenList.IndexOf(sl);
                            int ix           = ixVar.getIx3(ixP, ixT, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count);
                            Sell[ix].VarName = "SELL_PROD: " + pl.PROD + " T: " + tl.T + " SCEN: " + sl.SCEN;
                        });
                    });
                });

                //MAKE => 0 (constrain)
                //GRB.CONTINUOUS
                for (int a = 0; a < Make.Length; a++)
                {
                    expr1.Clear();
                    expr1.AddTerm(1, Make[a]);
                    gModel.AddConstr(expr1, GRB.GREATER_EQUAL, 0, ">=0");
                }

                //INV => 0 (constrain)
                for (int a = 0; a < Inv.Length; a++)
                {
                    expr1.Clear();
                    expr1.AddTerm(1, Inv[a]);
                    gModel.AddConstr(expr1, GRB.GREATER_EQUAL, 0, ">=0");
                }

                //SELL => 0 (constrain)
                for (int a = 0; a < Sell.Length; a++)
                {
                    expr1.Clear();
                    expr1.AddTerm(1, Sell[a]);
                    gModel.AddConstr(expr1, GRB.GREATER_EQUAL, 0, ">=0");
                }

                //SELL <= market[p,t]
                expr1.Clear();
                inputData.ProdList.ForEach(pl => {
                    Tlist2.ForEach(tl => {
                        inputData.ScenList.ForEach(sl => {
                            double market = inputData
                                            .MarketList
                                            .Find(ml =>
                                                  ml.PROD.Equals(pl.PROD) &&
                                                  ml.T.Equals(tl.T)).MARKET;

                            int ixP = inputData.ProdList.IndexOf(pl);
                            int ixT = Tlist2.IndexOf(tl);
                            int ixS = inputData.ScenList.IndexOf(sl);

                            int index = ixVar.getIx3(ixP, ixT, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count);
                            expr1.Clear();
                            expr1.AddTerm(1, Sell[index]);
                            gModel.AddConstr(expr1, GRB.LESS_EQUAL, market, "<=Market");
                        });
                    });
                });

                //Construir funcion objetivo
                //maximize Stage2_Profit:
                //   sum {s in SCEN} prob[s] *
                //      sum {p in PROD, t in 2..T} (revenue[p,t,s]*Sell[p,t,s] -
                //         prodcost[p]*Make[p,t,s] - invcost[p]*Inv[p,t,s]);
                expr1.Clear();
                inputData.ScenList.ForEach(s =>
                {
                    double prob = inputData.ProbList.Find(x => x.SCEN.Equals(s.SCEN)).PROB;

                    inputData.ProdList.ForEach(p =>
                    {
                        Tlist2.ForEach(tl =>
                        {
                            //SELL
                            double revenue = inputData
                                             .RevenueList
                                             .Find(r =>
                                                   r.PROD.Equals(p.PROD) &&
                                                   r.T.Equals(tl.T) &&
                                                   r.SCEN.Equals(s.SCEN)
                                                   ).REVENUE;

                            double prodCost = inputData
                                              .ProdCostList
                                              .Find(pc =>
                                                    pc.PROD.Equals(p.PROD)
                                                    ).PRODCOST;

                            double invCost = inputData
                                             .InvCostList
                                             .Find(ic =>
                                                   ic.PROD.Equals(p.PROD)
                                                   ).INVCOST;

                            int ixP = inputData.ProdList.IndexOf(p);
                            int ixT = Tlist2.IndexOf(tl);
                            int ixS = inputData.ScenList.IndexOf(s);

                            int indexVar = ixVar.getIx3(ixP, ixT, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count);

                            expr1.AddTerm(prob * revenue, Sell[indexVar]);
                            expr1.AddTerm(-1 * prob * prodCost, Make[indexVar]);
                            expr1.AddTerm(-1 * prob * invCost, Inv[indexVar]);
                        });
                    });
                });

                //Insertar funcion objetivo
                gModel.SetObjective(expr1, GRB.MAXIMIZE);

                //Insertar Restricciones

                //subject to Time {t in 2..T, s in SCEN}:
                //sum {p in PROD} (1/rate[p]) * Make[p,t,s] <= avail[t];
                expr1.Clear();
                Tlist2.ForEach(tl => {
                    inputData.ScenList.ForEach(sl =>
                    {
                        inputData.ProdList.ForEach(pl =>
                        {
                            double rate = 1 / inputData.RateList.Find(rl => rl.PROD.Equals(pl.PROD)).RATE;

                            int ixP = inputData.ProdList.IndexOf(pl);
                            int ixT = Tlist2.IndexOf(tl);
                            int ixS = inputData.ScenList.IndexOf(sl);

                            int ixMake = ixVar.getIx3(ixP, ixT, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count);

                            expr1.AddTerm(rate, Make[ixMake]);
                        });

                        double avail = inputData.AvailList.Find(al => al.T.Equals(tl.T)).AVAIL;
                        gModel.AddConstr(expr1, GRB.LESS_EQUAL, avail, "TIME_" + tl.T + "_" + sl.SCEN);
                    });
                });

                //subject to Balance2 { p in PROD, s in SCEN}:
                //Make[p, 2, s] + inv1[p] = Sell[p, 2, s] + Inv[p, 2, s];
                //2 => index T => 0
                expr1.Clear();
                inputData.ProdList.ForEach(pl =>
                {
                    inputData.ScenList.ForEach(sl =>
                    {
                        int ixP = inputData.ProdList.IndexOf(pl);
                        int ixT = 0;
                        int ixS = inputData.ScenList.IndexOf(sl);
                        int ix  = ixVar.getIx3(ixP, ixT, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count);

                        double inv1 = subModelParams.inv1.Find(il => il.PROD.Equals(pl.PROD)).INV0;

                        expr1.Clear();
                        expr2.Clear();

                        //como sumar un param ????
                        expr1.AddTerm(1, Make[ix]);
                        //expr1.AddTerm(inv1, null); ???? como??? // Se despeja vars & params
                        expr1.AddTerm(-1, Sell[ix]);
                        expr1.AddTerm(-1, Inv[ix]);

                        gModel.AddConstr(expr1, GRB.EQUAL, inv1, "BALANCE2_" + pl.PROD + "_" + sl.SCEN);
                    });
                });

                //subject to Balance { p in PROD, t in 3..T, s in SCEN}:
                //Make[p, t, s] + Inv[p, t - 1, s] = Sell[p, t, s] + Inv[p, t, s];

                inputData.ProdList.ForEach(pl =>
                {
                    Tlist3.ForEach(tl =>
                    {
                        inputData.ScenList.ForEach(sl =>
                        {
                            int ixP = inputData.ProdList.IndexOf(pl);
                            int ixT = Tlist2.IndexOf(tl);
                            int ixS = inputData.ScenList.IndexOf(sl);
                            int ix  = ixVar.getIx3(ixP, ixT, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count);
                            int ix2 = ixVar.getIx3(ixP, ixT - 1, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count);

                            expr1.Clear();
                            expr2.Clear();

                            expr1.AddTerm(1, Make[ix]);
                            expr1.AddTerm(1, Inv[ix2]);

                            expr2.AddTerm(1, Sell[ix]);
                            expr2.AddTerm(1, Inv[ix]);

                            gModel.AddConstr(expr1, GRB.EQUAL, expr2, "SUBJECT TO BALANCE");
                        });
                    });
                });

                //carpeta donde se expande el modelo
                gModel.Write(outputFolder + "Submodel_Model.lp");

                // RESOLVER EL MODELO
                try
                {
                    Console.WriteLine("Solving the sub model with gurobi..");
                    gModel.Optimize();

                    if (gModel.Status == 2)
                    {
                        for (int m = 0; m < Make.Length; m++)
                        {
                            Console.WriteLine("Make Var: " + Make[m].VarName + " = " + Make[m].X);
                        }

                        for (int m = 0; m < Inv.Length; m++)
                        {
                            Console.WriteLine("Inv Var: " + Inv[m].VarName + " = " + Inv[m].X);
                        }

                        for (int m = 0; m < Sell.Length; m++)
                        {
                            Console.WriteLine("Sell Var: " + Sell[m].VarName + " = " + Sell[m].X);
                        }
                    }

                    SubModelOutputs smo = new SubModelOutputs()
                    {
                        make         = Make,
                        sell         = Sell,
                        inv          = Inv,
                        stage2Profit = gModel.ObjVal,
                        gModel       = gModel
                    };

                    //gModel.Dispose();
                    //env.Dispose();

                    return(smo);
                }
                catch { Console.WriteLine("ERROR SOLVING THE MODEL"); }

                gModel.Dispose();
                env.Dispose();
            }

            catch (GRBException ex)
            {
                Console.WriteLine("Error code: " + ex.ErrorCode + ". " + ex.Message);
            }


            return(new SubModelOutputs());
        }
        void BuildGRBModel()
        {
            _env = new GRBEnv("SolutionLog.log");
            _env.Set(GRB.DoubleParam.MIPGap, 0.0);
            _env.Set(GRB.DoubleParam.TimeLimit, 500);
            _env.Set(GRB.DoubleParam.Heuristics, 0.5);
            _grbModel = new GRBModel(_env);

            //决策变量
            foreach (Node n in Data.NodeSet)
            {
                n.Result_GenerateFlow = _grbModel.AddVar(0.0, Data.M, 0.0, GRB.CONTINUOUS, "g_" + n.ID);
                n.Result_IsServerLoacationSelected = _grbModel.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "x_" + n.ID);
            }
            foreach (Arc a in Data.ArcSet)
            {
                a.Result_FlowF = _grbModel.AddVar(0.0, Data.M, 0.0, GRB.CONTINUOUS, "fF_" + a.FromNode.ID + "_" + a.ToNode.ID);
                a.Result_FlowR = _grbModel.AddVar(0.0, Data.M, 0.0, GRB.CONTINUOUS, "fR_" + a.FromNode.ID + "_" + a.ToNode.ID);
            }
            _grbModel.Update();

            //目标函数
            GRBLinExpr expr1 = 0;

            foreach (Node n in Data.NodeSet)
            {
                expr1 += n.Result_IsServerLoacationSelected * Data.ServerInstalationFee;
            }
            GRBLinExpr expr2 = 0;

            foreach (Arc a in Data.ArcSet)
            {
                expr2 += (a.Result_FlowF + a.Result_FlowR) * Data.FlowFeePerUnit;
            }

            _grbModel.SetObjective(expr1 + expr2, GRB.MINIMIZE);


            //约束条件
            foreach (Node n in Data.NodeSet)
            {
                _grbModel.AddConstr(n.Result_GenerateFlow <= n.Result_IsServerLoacationSelected * Data.M, "ct1_" + n.ID);
            }
            foreach (Node n in Data.NodeSet)
            {
                GRBLinExpr sum1 = 0;
                GRBLinExpr sum2 = 0;
                GRBLinExpr sum3 = 0;
                GRBLinExpr sum4 = 0;
                foreach (Arc a in n.ArcSet)
                {
                    if (a.ToNode == n)//进
                    {
                        sum1 += a.Result_FlowF;
                        sum3 += a.Result_FlowR;
                    }
                    else//出
                    {
                        sum2 += a.Result_FlowR;
                        sum4 += a.Result_FlowF;
                    }
                }
                _grbModel.AddConstr(n.Result_GenerateFlow + sum1 + sum2 == n.Demand + sum3 + sum4, "ct2_" + n.ID);
            }
            foreach (Arc a in Data.ArcSet)
            {
                _grbModel.AddConstr(a.Result_FlowF + a.Result_FlowR <= a.Capacity, "ct3_" + a.FromNode.ID + "_" + a.ToNode.ID);
            }
        }
Esempio n. 12
0
        public static Graph RunSolver(Graph graph)
        {
            GRBEnv env = new GRBEnv();
            env.Set(GRB.IntParam.OutputFlag, 0);
            env.Set(GRB.IntParam.LogToConsole, 0);
            env.Set(GRB.IntParam.Presolve, 2);
            env.Set(GRB.DoubleParam.Heuristics, 0.0);
            GRBModel model = new GRBModel(env);
            GRBVar[] variables = new GRBVar[graph.NumberOfEdges];
            model.SetCallback(new LPSolverCallback());
            Dictionary<Edge, GRBVar> edgeVars = new Dictionary<Edge, GRBVar>();

            // Add variables to the LP model
            for (int i = 0; i < graph.NumberOfEdges; i++)
            {
                variables[i] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "x_" + i);
                edgeVars.Add(graph.Edges[i], variables[i]);
            }
            model.Update();

            // Add constraints to the LP model
            Console.Write("\rRunning LP. Creating constraints...\r");
            //var nonTerminals = graph.Vertices.Except(graph.Terminals).ToList();
            ulong conNr = 0;
            //var terminalCombinations = new List<List<Vertex>>();

            // Assume, without loss of generality, that Terminals[0] is the root, and thus is always included
            int rootNr = 1;
            foreach (var rootTerminal in graph.Terminals)
            //var rootTerminal = graph.Terminals[0];
            {
                Console.Write("\rRunning LP. Creating constraints... {0}/{1}\r", rootNr, graph.Terminals.Count);
                foreach (var combination in GetBFS(graph, rootTerminal))
                {
                    var nodes = combination.ToList(); //new HashSet<Vertex>(combination);
                    if (nodes.Count == graph.NumberOfVertices || graph.Terminals.All(nodes.Contains))
                        continue;
                    //Debug.WriteLine("Combination: {0}", string.Join(" ", nodes));
                    //for (int i = 1; i <= nodes.Count; i++)
                    {
                        var edges = nodes//.Take(i)
                                         .SelectMany(graph.GetEdgesForVertex)
                                         .Distinct()
                                         .Where(x => x.WhereOne(y => !nodes.Contains(y)));
                        GRBLinExpr expression = 0;
                        foreach (var edge in edges)
                            expression.AddTerm(1, edgeVars[edge]);
                        model.AddConstr(expression >= 1.0, "subset_" + conNr);
                        conNr++;

                        if (conNr % 100000 == 0)
                        {
                            //model = model.Presolve(); //Pre-solve the model every 1000 constraints.
                            int constrBefore = model.GetConstrs().Length, varsBefore = model.GetVars().Length;
                            Debug.WriteLine("Presolve called.");
                            var presolved = model.Presolve();
                            Debug.WriteLine("Model has {0} constraints, {1} variables. Presolve has {2} constraints, {3} variables",
                                constrBefore, varsBefore, presolved.GetConstrs().Length, presolved.GetVars().Length);
                        }
                    }
                }

                //Debug.WriteLine("   ");
                //Debug.WriteLine("   ");
                rootNr++;
            }

            //terminalCombinations.Add(new List<Vertex>(new[] { graph.Terminals[0] }));
            //for (int j = 1; j < graph.Terminals.Count - 1; j++)
            //    terminalCombinations.AddRange(new Combinations<Vertex>(graph.Terminals.Skip(1), j).Select(combination => combination.Union(new[] { graph.Terminals[0] }).ToList()));

            //long nonTerminalSetsDone = 0;
            //long nonTerminalSets = 0;
            //for (int i = 0; i <= nonTerminals.Count; i++)
            //    nonTerminalSets += Combinations<Vertex>.NumberOfCombinations(nonTerminals.Count, i);

            //for (int i = 0; i <= nonTerminals.Count; i++)
            //{
            //    foreach (var nonTerminalSet in new Combinations<Vertex>(nonTerminals, i))
            //    {
            //        foreach (var nodes in (from a in terminalCombinations
            //                               select new HashSet<Vertex>(a.Union(nonTerminalSet))))
            //        {
            //            var edges = nodes.SelectMany(graph.GetEdgesForVertex)
            //                             .Distinct()
            //                             .Where(x => x.WhereOne(y => !nodes.Contains(y)));
            //            GRBLinExpr expression = 0;
            //            foreach (var edge in edges)
            //                expression.AddTerm(1, edgeVars[edge]);
            //            model.AddConstr(expression >= 1.0, "subset_" + conNr);
            //            conNr++;
            //        }
            //        nonTerminalSetsDone++;
            //        if (nonTerminalSetsDone % 100 == 0)
            //            Console.Write("\rRunning LP. Creating constraints... {0}/{1} ({2:0.000}%)\r", nonTerminalSetsDone, nonTerminalSets, nonTerminalSetsDone * 100.0 / nonTerminalSets);
            //    }
            //}

            // Solve the LP model
            Console.Write("\rRunning LP. Creating objective & updating...                                   \r");
            GRBLinExpr objective = new GRBLinExpr();
            for (int i = 0; i < graph.NumberOfEdges; i++)
                objective.AddTerm(graph.Edges[i].Cost, variables[i]);
            model.SetObjective(objective, GRB.MINIMIZE);
            Console.Write("\rRunning LP. Tuning...                                   \r");
            model.Tune();
            Debug.WriteLine("Presolve called.");
            model.Presolve();
            Console.Write("\rRunning LP. Solving...                               \r");
            Debug.WriteLine("Optimize called.");
            model.Optimize();

            Graph solution = graph.Clone();
            HashSet<Edge> includedEdges = new HashSet<Edge>();
            for (int i = 0; i < solution.NumberOfEdges; i++)
            {
                var value = variables[i].Get(GRB.DoubleAttr.X);
                if (value == 1)
                    includedEdges.Add(solution.Edges[i]);
            }

            foreach (var edge in solution.Edges.ToList())
                if (!includedEdges.Contains(edge))
                    solution.RemoveEdge(edge);

            Console.Write("\r                                                  \r");

            return solution;
        }
Esempio n. 13
0
        public Dictionary <EdgePair, Double> Solve(FCTPGraph ti)
        {
            Dictionary <EdgePair, Double> edgeFlows = new Dictionary <EdgePair, Double>();
            Dictionary <String, Edge>     edgeMap   = new Dictionary <String, Edge>();
            Dictionary <String, GRBVar>   varMap    = new Dictionary <String, GRBVar>();
            String currentVar = "";

            try {
                //Model
                GRBEnv env = new GRBEnv();
                env.Set(GRB.IntParam.LogToConsole, 0);
                GRBModel model = new GRBModel(env);
                model.Set(GRB.StringAttr.ModelName, "tranportation");


                //edges
                foreach (Edge e in ti.Edges)
                {
                    String xij = edgeVarName(e.Source, e.Sink);
                    edgeMap.Add(xij, e);
                    GRBVar var = model.AddVar(0, GRB.INFINITY, e.C, GRB.CONTINUOUS, xij);
                    varMap.Add(xij, var);
                }
                //objective min Sum cij xij
                model.Set(GRB.IntAttr.ModelSense, GRB.MINIMIZE);

                //integrate variables
                model.Update();

                //supply constraints
                foreach (Node source in ti.Sources)
                {
                    GRBLinExpr sourceConstraint = new GRBLinExpr();
                    foreach (Node sink in ti.Sinks)
                    {
                        String name = edgeVarName(source.Id, sink.Id);
                        sourceConstraint.AddTerm(1, varMap[name]);
                    }
                    model.AddConstr(sourceConstraint, GRB.EQUAL, source.Amount, "");
                }
                //demand constraints
                foreach (Node sink in ti.Sinks)
                {
                    GRBLinExpr sinkConstraint = new GRBLinExpr();
                    foreach (Node source in ti.Sources)
                    {
                        String name = edgeVarName(source.Id, sink.Id);
                        sinkConstraint.AddTerm(1, varMap[name]);
                    }
                    model.AddConstr(sinkConstraint, GRB.EQUAL, sink.Amount, "");
                }
                //update constraints
                model.Update();

                model.Write("mipTranportationGurobi.lp");
                env.Set(GRB.IntParam.Threads, 1);
                model.Optimize();

                bool status = model.Get(GRB.IntAttr.Status) == GRB.Status.OPTIMAL;
                Console.WriteLine("Status: " + status);
                foreach (String s in edgeMap.Keys)
                {
                    currentVar = s;
                    double   flow = varMap[s].Get(GRB.DoubleAttr.X);
                    Edge     e    = edgeMap[s];
                    EdgePair ep   = new EdgePair(e.Source, e.Sink);
                    edgeFlows.Add(ep, flow);
                }
                model.Dispose();
            } catch (GRBException e) {
                Console.Out.WriteLine(currentVar + " - is current var");
                Console.Out.WriteLine(e.ErrorCode);
                Console.Out.WriteLine(e.ToString());
            }

            return(edgeFlows);
        }
Esempio n. 14
0
        public static void build_initial_model(bool lp_relax)
        {
            env = new GRBEnv(true);
            env.Set("LogFile", "Recoloracao.log");
            env.Set(GRB.IntParam.LogToConsole, 0);
            env.Start();

            model           = new GRBModel(env);
            model.ModelName = "Recoloracao";
            model.Set(GRB.IntParam.Threads, 1);
            model.Set(GRB.IntParam.Method, 1);
            model.Set(GRB.DoubleParam.TimeLimit, 1800);

            X = new GRBVar[input.nVertices, input.nCores];
            var obj = new GRBLinExpr();

            for (int i = 0; i < input.nVertices; i++)
            {
                for (int j = 0; j < input.nCores; j++)
                {
                    string nome = "X_ " + i.ToString() + "_c " + j.ToString();

                    if (lp_relax)
                    {
                        X[i, j] = model.AddVar(0.0, 1.0, 1.0, GRB.CONTINUOUS, nome);
                    }
                    else
                    {
                        X[i, j] = model.AddVar(0.0, 1.0, 1.0, GRB.BINARY, nome);
                    }

                    if (input.caminhoColorido[i] != j + 1)
                    {
                        obj.AddTerm(1, X[i, j]);
                    }
                }
            }

            model.SetObjective(obj, GRB.MINIMIZE);

            // Restrição 1
            for (int i = 0; i < input.nVertices; i++)
            {
                GRBLinExpr soma = new GRBLinExpr();

                for (int c = 0; c < input.nCores; c++)
                {
                    soma.AddTerm(1.0, X[i, c]);
                }

                model.AddConstr(soma, GRB.EQUAL, 1, "Cor vértice" + i);
            }

            // Restrição 2
            for (int p = 0; p < input.nVertices - 2; p++)
            {
                for (int r = p + 2; r < input.nVertices; r++)
                {
                    for (int q = p + 1; q < r; q++)
                    {
                        for (int k = 0; k < input.nCores; k++)
                        {
                            model.AddConstr(X[p, k] - X[q, k] + X[r, k], GRB.LESS_EQUAL, 1, "p" + p + "-q" + q + "-r" + r + "-k" + k);
                        }
                    }
                }
            }
        }
Esempio n. 15
0
        public Schedule Run()
        {
            Schedule schedule = new Schedule(100);

            try
            {
                GRBEnv env = new GRBEnv(true);
                env.Set("LogFile", "mip1.log");
                env.Start();
                GRBModel model = new GRBModel(env);

                // Create variables
                GRBVar[,] varInstructors = new GRBVar[ctx.Instructors.Length, 100];
                GRBVar[,] varStudents    = new GRBVar[ctx.Students.Length, 100];

                GRBVar[,] varPresidentsSessions  = new GRBVar[ctx.Presidents.Length, 20];
                GRBVar[,] varSecretariesSessions = new GRBVar[ctx.Secretaries.Length, 20];

                GRBVar[] varPresidentsTempP = new GRBVar[ctx.Presidents.Length];
                GRBVar[] varPresidentsTempQ = new GRBVar[ctx.Presidents.Length];

                GRBVar[] varSecretariesTempP = new GRBVar[ctx.Secretaries.Length];
                GRBVar[] varSecretariesTempQ = new GRBVar[ctx.Secretaries.Length];

                GRBVar[] varMembersTempP = new GRBVar[ctx.Members.Length];
                GRBVar[] varMembersTempQ = new GRBVar[ctx.Members.Length];

                for (int ts = 0; ts < 100; ts++)
                {
                    for (int i = 0; i < ctx.Instructors.Length; i++)
                    {
                        varInstructors[i, ts] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, ctx.Instructors[i].Name + " " + ts);
                    }

                    for (int s = 0; s < ctx.Students.Length; s++)
                    {
                        varStudents[s, ts] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, ctx.Students[s].Name + " " + ts);
                    }
                }

                for (int session = 0; session < 20; session++)
                {
                    for (int president = 0; president < ctx.Presidents.Length; president++)
                    {
                        varPresidentsSessions[president, session] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, ctx.Presidents[president].Name + "_session_" + session);
                    }
                    for (int secretary = 0; secretary < ctx.Secretaries.Length; secretary++)
                    {
                        varSecretariesSessions[secretary, session] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, ctx.Secretaries[secretary].Name + "_session_" + session);
                    }
                }

                for (int president = 0; president < ctx.Presidents.Length; president++)
                {
                    varPresidentsTempP[president] = model.AddVar(0.0, GRB.INFINITY, 0.0, GRB.INTEGER, ctx.Presidents[president].Name + "_workload_P");
                    varPresidentsTempQ[president] = model.AddVar(0.0, GRB.INFINITY, 0.0, GRB.INTEGER, ctx.Presidents[president].Name + "_workload_Q");
                }

                for (int secretary = 0; secretary < ctx.Secretaries.Length; secretary++)
                {
                    varSecretariesTempP[secretary] = model.AddVar(0.0, GRB.INFINITY, 0.0, GRB.INTEGER, ctx.Secretaries[secretary].Name + "_workload_P");
                    varSecretariesTempQ[secretary] = model.AddVar(0.0, GRB.INFINITY, 0.0, GRB.INTEGER, ctx.Secretaries[secretary].Name + "_workload_Q");
                }

                for (int member = 0; member < ctx.Members.Length; member++)
                {
                    varMembersTempP[member] = model.AddVar(0.0, GRB.INFINITY, 0.0, GRB.INTEGER, ctx.Members[member].Name + "workload_P");
                    varMembersTempQ[member] = model.AddVar(0.0, GRB.INFINITY, 0.0, GRB.INTEGER, ctx.Members[member].Name + "workload_Q");
                }


                // Set objective
                model.SetObjective(SumProduct(varInstructors, ctx.Instructors)
                                   + SumOfVars(varPresidentsTempP) + SumOfVars(varPresidentsTempQ)
                                   + SumOfVars(varSecretariesTempP) + SumOfVars(varSecretariesTempQ)
                                   + SumOfVars(varMembersTempP) + SumOfVars(varMembersTempQ)
                                   , GRB.MINIMIZE);

                char[] equalArray   = Enumerable.Range(0, 100).Select(x => GRB.EQUAL).ToArray();
                char[] greaterArray = Enumerable.Range(0, 100).Select(x => GRB.GREATER_EQUAL).ToArray();
                char[] lessArray    = Enumerable.Range(0, 100).Select(x => GRB.LESS_EQUAL).ToArray();

                // Add constraint: max 5 instructors
                string[] nameOfMaxNrInstructorsConstrs = Enumerable.Range(0, 100).Select(x => "MaxInstructorsNr" + x).ToArray();
                model.AddConstrs(SumOfPersonVarsPerTs(varInstructors), lessArray, NrArray(5.0), nameOfMaxNrInstructorsConstrs);

                // Add constraint: be min a president in every ts (enélkül több elnök is lehet 1 ts-ban)
                string[] nameOfPresidentsConstrs = Enumerable.Range(0, 100).Select(x => "President" + x).ToArray();
                model.AddConstrs(SumOfPersonVarsPerTs(GetPresidentsVars(varInstructors)), equalArray, NrArray(1.0), nameOfPresidentsConstrs);

                // Add constraint: be min a secretary in every ts
                string[] nameOfSecretariesConstrs = Enumerable.Range(0, 100).Select(x => "Secretary" + x).ToArray();
                model.AddConstrs(SumOfPersonVarsPerTs(GetSecretariesVars(varInstructors)), equalArray, NrArray(1.0), nameOfSecretariesConstrs);

                // Add constraint: be min a member in every ts
                string[] nameOfMembersConstrs = Enumerable.Range(0, 100).Select(x => "Member" + x).ToArray();
                model.AddConstrs(SumOfPersonVarsPerTs(GetMembersVars(varInstructors)), greaterArray, NrArray(1.0), nameOfMembersConstrs);

                string[] nameOfMembersMaxConstrs = Enumerable.Range(0, 100).Select(x => "MemberMax" + x).ToArray();
                model.AddConstrs(SumOfPersonVarsPerTs(GetMembersVars(varInstructors)), lessArray, NrArray(2.0), nameOfMembersMaxConstrs);

                // Add constraint: be a student in every ts
                string[] nameOfStudentsPerTsConstrs = Enumerable.Range(0, 100).Select(x => "Student" + x).ToArray();
                model.AddConstrs(SumOfPersonVarsPerTs(varStudents), equalArray, NrArray(1.0), nameOfStudentsPerTsConstrs);

                string[] nameOfStudentsConstrs = Enumerable.Range(0, 100).Select(x => "Student" + x).ToArray();
                model.AddConstrs(SumOfPersonVarsPerPerson(varStudents), equalArray, NrArray(1.0), nameOfStudentsConstrs);

                // Add constraint: be the supervisor of student there
                for (int ts = 0; ts < varStudents.GetLength(1); ts++)
                {
                    for (int student = 0; student < varStudents.GetLength(0); student++)
                    {
                        int idOfSupervisor = ctx.Students[student].Supervisor.Id;
                        model.AddConstr(varStudents[student, ts] - varInstructors[idOfSupervisor, ts] <= 0.0, "Supervisor" + ts + "_" + student);
                    }
                }

                // Add constraint: be examiner there
                for (int student = 0; student < varStudents.GetLength(0); student++)
                {
                    GRBLinExpr[] sumOfExaminersVarsPerTs = SumOfPersonVarsPerTs(GetExaminersVars(varInstructors, ctx.Students[student].ExamCourse));
                    for (int ts = 0; ts < varStudents.GetLength(1); ts++)
                    {
                        model.AddConstr(varStudents[student, ts] - sumOfExaminersVarsPerTs[ts] <= 0.0, "Examiner" + ts + "_" + student);
                    }
                }


                // Add constraint: president not change

                GRBVar[,] presidentsVars = GetPresidentsVars(varInstructors);

                for (int session = 0; session < 20; session++)
                {
                    for (int president = 0; president < ctx.Presidents.Length; president++)
                    {
                        GRBVar[] presidentsVarsInSession = new GRBVar[]
                        {
                            presidentsVars[president, session *5],
                            presidentsVars[president, session *5 + 1],
                            presidentsVars[president, session *5 + 2],
                            presidentsVars[president, session *5 + 3],
                            presidentsVars[president, session *5 + 4]
                        };
                        model.AddGenConstrAnd(varPresidentsSessions[president, session], presidentsVarsInSession, "PresindentInSession" + president + "_" + session);
                    }
                }
                string[] nameOfPresidentsSessionsConstraints = Enumerable.Range(0, 100).Select(x => "PresidentSession" + x).ToArray();
                model.AddConstrs(SumOfPersonVarsPerTs(varPresidentsSessions), equalArray, NrArray(1.0), nameOfPresidentsSessionsConstraints);

                // Add constraint: secretary not change

                GRBVar[,] secretariesVars = GetSecretariesVars(varInstructors);

                for (int session = 0; session < 20; session++)
                {
                    for (int secretary = 0; secretary < ctx.Secretaries.Length; secretary++)
                    {
                        GRBVar[] secretariesVarsInSession = new GRBVar[]
                        {
                            secretariesVars[secretary, session *5],
                            secretariesVars[secretary, session *5 + 1],
                            secretariesVars[secretary, session *5 + 2],
                            secretariesVars[secretary, session *5 + 3],
                            secretariesVars[secretary, session *5 + 4]
                        };
                        model.AddGenConstrAnd(varSecretariesSessions[secretary, session], secretariesVarsInSession, "SecretaryInSession" + secretary + "_" + session);
                    }
                }
                string[] nameOfSecretariesSessionsConstraints = Enumerable.Range(0, 100).Select(x => "SecretariesSession" + x).ToArray();
                model.AddConstrs(SumOfPersonVarsPerTs(varSecretariesSessions), equalArray, NrArray(1.0), nameOfSecretariesSessionsConstraints);

                // Add constraint: presidents available
                model.AddConstr(SumProduct(GetPresidentsVars(varInstructors), ctx.Presidents) == 0.0, "PresindetsAvailable");

                // Add constraint: secretaries available
                model.AddConstr(SumProduct(GetSecretariesVars(varInstructors), ctx.Secretaries) == 0.0, "SecretariesAvailable");


                // Add constraint: workload of presidents

                string[] nameOfPresidentWorkloadMinConstrs = Enumerable.Range(0, 100).Select(x => "PresintWorkloadMin" + x).ToArray();
                string[] nameOfPresidentWorkloadMaxConstrs = Enumerable.Range(0, 100).Select(x => "PresintWorkloadMax" + x).ToArray();
                model.AddConstrs(SumOfPersonVarsPerPerson(varPresidentsSessions), greaterArray, NrArray(3.0), nameOfPresidentWorkloadMinConstrs);
                model.AddConstrs(SumOfPersonVarsPerPerson(varPresidentsSessions), lessArray, NrArray(7.0), nameOfPresidentWorkloadMaxConstrs);

                for (int president = 0; president < ctx.Presidents.Length; president++)
                {
                    // for soft constraint:
                    model.AddConstr(varPresidentsTempP[president] - varPresidentsTempQ[president] == SumOfPersonVarsPerPerson(varPresidentsSessions)[president] - 5.0, "workloadSoft" + president);
                }

                // Add constraint: workload of secretaries

                string[] nameOfSecretaryWorkloadMinConstrs = Enumerable.Range(0, 100).Select(x => "SecretaryWorkloadMin" + x).ToArray();
                string[] nameOfSecretaryWorkloadMaxConstrs = Enumerable.Range(0, 100).Select(x => "SecretaryWorkloadMax" + x).ToArray();
                model.AddConstrs(SumOfPersonVarsPerPerson(varSecretariesSessions), greaterArray, NrArray(1.0), nameOfSecretaryWorkloadMinConstrs);
                model.AddConstrs(SumOfPersonVarsPerPerson(varSecretariesSessions), lessArray, NrArray(3.0), nameOfSecretaryWorkloadMaxConstrs);

                for (int secretary = 0; secretary < ctx.Secretaries.Length; secretary++)
                {
                    // for soft constraint:
                    model.AddConstr(varSecretariesTempP[secretary] - varSecretariesTempQ[secretary] == SumOfPersonVarsPerPerson(varSecretariesSessions)[secretary] - 2.0, "workloadSoft" + secretary);
                }

                // Add constraint: workload of members

                string[] nameOfMemberWorkloadMinConstrs = Enumerable.Range(0, 100).Select(x => "MemberWorkloadMin" + x).ToArray();
                string[] nameOfMemberWorkloadMaxConstrs = Enumerable.Range(0, 100).Select(x => "MemberWorkloadMax" + x).ToArray();
                model.AddConstrs(SumOfPersonVarsPerPerson(GetMembersVars(varInstructors)), greaterArray, NrArray(7.0), nameOfMemberWorkloadMinConstrs);
                //model.AddConstrs(SumOfPersonVarsPerPerson(GetMembersVars(varInstructors)), lessArray, NrArray(12.0), nameOfMemberWorkloadMaxConstrs);

                for (int member = 0; member < ctx.Members.Length; member++)
                {
                    // for soft constraint:
                    model.AddConstr(varMembersTempP[member] - varMembersTempQ[member] == SumOfPersonVarsPerPerson(GetMembersVars(varInstructors))[member] - 10.0, "workloadSoft" + member);
                }

                // Optimize model
                model.Optimize();

                for (int ts = 0; ts < 100; ts++)
                {
                    List <Instructor> instructorsTS = new List <Instructor>();
                    for (int person = 0; person < ctx.Instructors.Length; person++)
                    {
                        if (varInstructors[person, ts].X == 1.0)
                        {
                            instructorsTS.Add(ctx.Instructors[person]);
                        }
                    }
                    schedule.FinalExams[ts]    = new FinalExam();
                    schedule.FinalExams[ts].Id = ts;

                    //schedule.FinalExams[ts].President = instructorsTS.Find(i => i.Roles.HasFlag(Roles.President));
                    //schedule.FinalExams[ts].Secretary = instructorsTS.Find(i => i.Roles.HasFlag(Roles.Secretary));
                    schedule.FinalExams[ts].Member = instructorsTS.Find(i => i.Roles.HasFlag(Roles.Member));

                    for (int i = 0; i < ctx.Students.Length; i++)
                    {
                        if (varStudents[i, ts].X == 1.0)
                        {
                            schedule.FinalExams[ts].Student = ctx.Students[i];
                        }
                    }
                    if (instructorsTS.Contains(schedule.FinalExams[ts].Student.Supervisor))
                    {
                        schedule.FinalExams[ts].Supervisor = schedule.FinalExams[ts].Student.Supervisor;
                    }

                    Course courseOfStudent = schedule.FinalExams[ts].Student.ExamCourse;

                    for (int i = 0; i < courseOfStudent.Instructors.Length; i++)
                    {
                        if (instructorsTS.Contains(courseOfStudent.Instructors[i]))
                        {
                            schedule.FinalExams[ts].Examiner = courseOfStudent.Instructors[i];
                        }
                    }
                }

                for (int session = 0; session < 20; session++)
                {
                    Instructor presindetInSession = new Instructor();
                    Instructor secretaryInSession = new Instructor();
                    for (int president = 0; president < ctx.Presidents.Length; president++)
                    {
                        if (varPresidentsSessions[president, session].X == 1)
                        {
                            presindetInSession = ctx.Presidents[president];
                        }
                    }

                    for (int secretary = 0; secretary < ctx.Secretaries.Length; secretary++)
                    {
                        if (varSecretariesSessions[secretary, session].X == 1)
                        {
                            secretaryInSession = ctx.Secretaries[secretary];
                        }
                    }

                    for (int ts = session * 5 + 0; ts < session * 5 + 5; ts++)
                    {
                        schedule.FinalExams[ts].President = presindetInSession;
                        schedule.FinalExams[ts].Secretary = secretaryInSession;
                    }
                }

                Console.WriteLine("Obj: " + model.ObjVal);

                // Dispose of model and env
                model.Dispose();
                env.Dispose();
            }
            catch (GRBException e)
            {
                Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
            }


            return(schedule);
        }
Esempio n. 16
0
        public void RescheduleLotMachineAllocation()
        {
            //Console.WriteLine(GetTime);
            //Console.WriteLine("QueueLength:");
            //Console.WriteLine(Queue.Length);
            int limitILPSolver = 2000; // maximum number of jobs put into the ILP solver


            // Clear scheduled lots
            foreach (Machine machine in LithographyArea.Machines)
            {
                ScheduledLotsPerMachine[machine.Name].Clear();
            }

            // Set weight of each Lot
            for (int j = 0; j < Queue.Length; j++)
            {
                Lot peekLot = Queue.PeekAt(j);

                Queue.PeekAt(j).WeightDueDate    = GetDueDateWeight(peekLot);
                Queue.PeekAt(j).WeightWIPBalance = GetWIPBalanceWeight(peekLot);
            }


            try
            {
                // Get lots which have to be scheduled

                List <Lot> lotsToBeScheduled = new List <Lot>();


                if (Queue.Length <= limitILPSolver) // Small enough for ILP solver
                {
                    for (int j = 0; j < Queue.Length; j++)
                    {
                        // Peek next lot in queue
                        Lot peekLot = Queue.PeekAt(j);
                        lotsToBeScheduled.Add(peekLot);
                    }
                }
                else //Filter lots to get a set of 750 lots for ILP Solver
                {
                    lotsToBeScheduled = DecreaseQueueLength(limitILPSolver);
                }

                double[] readyTimes = GetReadyTimes();

                double[,] processingTimes = GetProcessingTimes(lotsToBeScheduled);

                List <Lot> lotsToBeScheduledNew = RemoveLotsCurrentlyNotEligible(lotsToBeScheduled, processingTimes);

                if (lotsToBeScheduledNew.Count < lotsToBeScheduled.Count)
                {
                    processingTimes   = GetProcessingTimes(lotsToBeScheduledNew);
                    lotsToBeScheduled = lotsToBeScheduledNew;
                }

                // Number of jobs and machines
                int nMachines = processingTimes.GetLength(0);
                int nJobs     = processingTimes.GetLength(1);

                Console.Write($"Time: {LithographyArea.StartDate.AddSeconds(GetTime)}, number of jobs scheduled: {nJobs}, ");

                // Create an empty environment, set options and start
                GRBEnv env = new GRBEnv(true);
                env.Set("LogFile", "mip1.log");
                //env.OutputFlag = 0;
                env.Start();

                // Create empty model
                GRBModel model = new GRBModel(env);

                // Decision Variable
                GRBVar[,,] x = new GRBVar[nMachines, nJobs, nJobs];
                for (int i = 0; i < nMachines; ++i)
                {
                    for (int j = 0; j < nJobs; ++j)
                    {
                        for (int k = 0; k < nJobs; ++k)
                        {
                            x[i, j, k] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "x");
                        }
                    }
                }

                // Set objective:
                GRBLinExpr exp1 = 0.0;

                for (int i = 0; i < nMachines; ++i)
                {
                    for (int j = 0; j < nJobs; ++j)
                    {
                        for (int k = 0; k < nJobs; ++k)
                        {
                            GRBLinExpr exp2 = ((k + 1) * processingTimes[i, j] + readyTimes[i]) * x[i, j, k];
                            exp1.Add(exp2);
                        }
                    }
                }

                model.SetObjective(exp1, GRB.MINIMIZE);

                // Constraints
                for (int j = 0; j < nJobs; ++j)
                {
                    GRBLinExpr exp3 = 0.0;

                    for (int i = 0; i < nMachines; ++i)
                    {
                        for (int k = 0; k < nJobs; ++k)
                        {
                            exp3.AddTerm(1.0, x[i, j, k]);
                        }
                    }
                    model.AddConstr(exp3 == 1.0, "c1");
                }

                for (int i = 0; i < nMachines; ++i)
                {
                    for (int k = 0; k < nJobs; ++k)
                    {
                        GRBLinExpr exp4 = 0.0;
                        for (int j = 0; j < nJobs; ++j)
                        {
                            exp4.AddTerm(1.0, x[i, j, k]);
                        }
                        model.AddConstr(exp4 <= 1.0, "c2");
                    }
                }

                // Solve
                model.Optimize();

                Console.WriteLine($"Status {model.Status}");

                for (int i = 0; i < nMachines; ++i)
                {
                    for (int j = 0; j < nJobs; j++)
                    {
                        Lot peekLot = lotsToBeScheduled[j];
                        for (int k = 0; k < nJobs; ++k)
                        {
                            if (x[i, j, k].X == 1.0)
                            {
                                //Console.WriteLine($"({i},{j},{k})" + "ProcessingTime: " + processingTimes[i, j]);

                                //TODO: Schedule on machine
                                ScheduledLotsPerMachine[LithographyArea.Machines[i].Name].Add(peekLot);
                            }
                        }
                    }
                }

                Console.WriteLine("Objective: " + model.ObjVal);

                // Dispose of model and env
                model.Dispose();
                env.Dispose();
            }
            catch (GRBException e)
            {
                Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
            }
        }
Esempio n. 17
0
        public override Schedule Generate(Scenario scenario, FilledBaseline baseline = null)
        {
            var batches          = scenario.Projects.SelectMany(p => p.Batches).OrderBy(b => b.Compatibility).ToArray();
            var batchesBothLines = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Both).ToArray();
            var batchesLine1     = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Line1).ToArray();
            var batchesLine2     = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Line2).ToArray();

            int       bBc          = batchesBothLines.Count();
            var       earliestDate = scenario.Projects.Min(p => p.DeliveryDate);
            var       lastDate     = scenario.Projects.Max(p => p.DeliveryDate.AddDays(7 * p.Batches.Count())).AddDays(7 * 3); // add an additional buffer of 3 weeks.
            int       maxWeek      = (lastDate - earliestDate).Days / 7;                                                       // 0 to 200
            const int weeksBuffer  = 3;

            var delayPenaltyMultiplier    = 0.01;            // 1% of revenue per week
            var interestPenaltyMultiplier = (0.045 / 52.14); // 4.5%  / 52.14 * (revenue – margin)


            var env = new GRBEnv();

            env.Set(GRB.IntParam.LogToConsole, 1);
            //env.Set(GRB.DoubleParam.TimeLimit, _config.TimeLimit.Value);
            //env.Set(GRB.DoubleParam.MIPGap, 0.02);

            var m = new GRBModel(env);

            // decide: which batch is when, on which line
            // TODO: Batch can be over two weeks
            var varBatchWeekLine1 = m.AddVars(batchesBothLines.Count() + batchesLine1.Count(), maxWeek, 0, 1, GRB.BINARY, "batchWeekLine1");
            var varBatchWeekLine2 = m.AddVars(batchesBothLines.Count() + batchesLine2.Count(), maxWeek, 0, 1, GRB.BINARY, "batchWeekLine2");

            m.Update();

            // line constraints:

            // assign batch only once (constraint for single lines then both lines)
            // if it's a fixed project though, then it must be allocated
            for (int l = 0; l < 2; l++)
            {
                var line = l == 0 ? varBatchWeekLine1 : varBatchWeekLine2;
                for (int bi = bBc; bi < line.GetLength(0); bi++)
                {
                    var batchTotal = new GRBLinExpr();
                    for (int w = 0; w < maxWeek; w++)
                    {
                        batchTotal += line[bi, w];
                    }
                    var  batch          = (l == 0 ? batchesLine1 : batchesLine2)[bi - bBc];
                    bool isFixedProject = scenario.Projects.First(p => p.Batches.Contains(batch)) is FixedProject;
                    if (!isFixedProject)
                    {
                        m.AddConstr(batchTotal <= 1, "assign batch only once");
                    }
                    else
                    {
                        m.AddConstr(batchTotal == 1, "assign batch exactly once");
                    }
                }
            }
            for (int bi = 0; bi < bBc; bi++)
            {
                var batchTotal = new GRBLinExpr();
                for (int w = 0; w < maxWeek; w++)
                {
                    batchTotal += varBatchWeekLine1[bi, w];
                    batchTotal += varBatchWeekLine2[bi, w];
                }
                var  batch          = batchesBothLines[bi];
                bool isFixedProject = scenario.Projects.First(p => p.Batches.Contains(batch)) is FixedProject;
                if (!isFixedProject)
                {
                    m.AddConstr(batchTotal <= 1, "assign batch only once");
                }
                else
                {
                    m.AddConstr(batchTotal == 1, "assign batch exactly once");
                }
            }


            // for all batches which aren't assigned to a line yet, limit the allocation of yet unassigned batches of a project to one line
            // TODO: If project has e.g. line1 and both lines, limit both lines to line 1?
            var varLineDecision = m.AddVars(scenario.Projects.Count(), 0, 1, GRB.BINARY, "varLineDecision");

            m.Update();
            for (int pi = 0; pi < scenario.Projects.Count(); pi++)
            {
                var p = scenario.Projects[pi];

                if (!p.Batches.Any(b => b.Compatibility == Batch.LineCompatibility.Both))
                {
                    continue;
                }

                var sumBatchLine1 = new GRBLinExpr();
                var sumBatchLine2 = new GRBLinExpr();
                int i             = batches.IndexOf(p.Batches.First());
                for (int j = 0; j < p.Batches.Count(); j++)
                {
                    if (p.Batches[j].Compatibility != Batch.LineCompatibility.Both)
                    {
                        continue;
                    }

                    for (int w = 0; w < maxWeek; w++)
                    {
                        sumBatchLine1 += varBatchWeekLine1[i + j, w];
                        sumBatchLine2 += varBatchWeekLine2[i + j, w];
                    }
                }
                m.AddConstr(sumBatchLine1 <= (1 - varLineDecision[pi]) * GRB.INFINITY);
                m.AddConstr(sumBatchLine2 <= varLineDecision[pi] * GRB.INFINITY);
            }

            // on each line the total of a week must not exceed 24*7
            for (int l = 0; l < 2; l++)
            {
                var lineVars = l == 0 ? varBatchWeekLine1 : varBatchWeekLine2;
                var line     = l == 0 ? batchesLine1 : batchesLine2;
                int bBC      = batchesBothLines.Count();

                for (int w = 0; w < maxWeek; w++)
                {
                    var weekTotal = new GRBLinExpr();
                    for (int b = 0; b < lineVars.GetLength(0); b++)
                    {
                        var batch = b < bBC ? batchesBothLines[b] : line[b - bBC];

                        weekTotal += lineVars[b, w] * batch.UsedWorkHours;

                        /*var test = new GRBLinExpr();
                         * test += (lineVars[b] == w) * 0.1;
                         * (l[b] - w) * batch.UsedWorkHours*/
                    }
                    m.AddConstr(weekTotal <= 24 * 7);
                }
            }

            // for each project, either all or no batches must be assigned
            var totalBatchesOfProject = new Dictionary <Project, List <GRBLinExpr> >();

            foreach (var p in scenario.Projects)
            {
                var allBatches = new List <GRBLinExpr>();

                // gather the total of all batches
                GRBLinExpr previousBatchTotal = null;
                for (int bi = 0; bi < p.Batches.Count(); bi++)
                {
                    var b          = p.Batches[bi];
                    var batchTotal = new GRBLinExpr();
                    if (b.Compatibility == Batch.LineCompatibility.Line1)
                    {
                        var bIndex = bBc + batchesLine1.IndexOf(b);
                        for (int w = 0; w < maxWeek; w++)
                        {
                            batchTotal += varBatchWeekLine1[bIndex, w];
                        }
                    }
                    else if (b.Compatibility == Batch.LineCompatibility.Line2)
                    {
                        var bIndex = bBc + batchesLine2.IndexOf(b);
                        for (int w = 0; w < maxWeek; w++)
                        {
                            batchTotal += varBatchWeekLine2[bIndex, w];
                        }
                    }
                    else
                    {
                        var bIndex = batchesBothLines.IndexOf(b);
                        for (int w = 0; w < maxWeek; w++)
                        {
                            batchTotal += varBatchWeekLine1[bIndex, w];
                            batchTotal += varBatchWeekLine2[bIndex, w];
                        }
                    }

                    // the sum of this batch over all weeks (0 or 1) has to be the same as the sum of the previous one
                    if (bi > 0)
                    {
                        m.AddConstr(previousBatchTotal == batchTotal);
                    }

                    previousBatchTotal = batchTotal;
                    allBatches.Add(batchTotal);
                }
                totalBatchesOfProject.Add(p, allBatches);
            }

            // DEPRECATED: penalty/interest term adds this constraint with slack
            // in-between batches of the same project, 3 weeks distance should exist, otherwise there will be a penalty

            /*foreach (var p in scenario.Projects)
             * {
             *  var bCompatibility = p.Batches.First().Compatibility;
             *
             *  // for all batches of this project make sure that the previous one is at least (3 + 1) weeks away
             *  // in other words: the total of batches within (3 + 1) weeks must be 4 at least.
             *  for (int w = 0; w < maxWeek - weeksBuffer; w++)
             *  {
             *      var batchTotalOver4Weeks = new GRBLinExpr();
             *      // sum all batches over a 4 week period
             *      for (int i = 0; i <= weeksBuffer; i++)
             *      {
             *          foreach (var b in p.Batches)
             *          {
             *              if (bCompatibility == Batch.LineCompatibility.Line1)
             *              {
             *                  batchTotalOver4Weeks += varBatchWeekLine1[bBc + batchesLine1.IndexOf(b), w + i];
             *              }
             *              else if (bCompatibility == Batch.LineCompatibility.Line2)
             *              {
             *                  batchTotalOver4Weeks += varBatchWeekLine2[bBc + batchesLine2.IndexOf(b), w + i];
             *              }
             *              else
             *              {
             *                  var bIndex = batchesBothLines.IndexOf(b);
             *                  batchTotalOver4Weeks += varBatchWeekLine1[bIndex, w + i];
             *                  batchTotalOver4Weeks += varBatchWeekLine2[bIndex, w + i];
             *              }
             *          }
             *      }
             *      m.AddConstr(batchTotalOver4Weeks <= 1);
             *  }
             * }*/

            // Tbd: Only half of the batch slots of line 1 may be occupied. Sometimes existst as internal projects.
            // fill gap between 50% and internal projects, monthly resolution



            // Maximize the margin (including delay and interest penalties) and the workload
            // TODO: Only first one important? no delay otherwise?
            var margin = new GRBLinExpr();

            foreach (var p in scenario.Projects)
            {
                // if the project is used add the margin
                margin += totalBatchesOfProject[p].First() * p.Margin;

                // deduct the delay penalty for each batch.
                int startWeekOfProject = (p.DeliveryDate - earliestDate).Days / 7;
                var varMaxedValue      = m.AddVars(p.Batches.Count(), 0, maxWeek, GRB.CONTINUOUS, "penaltyIndicator" + p.Description);
                var varDecisionVar     = m.AddVars(p.Batches.Count(), 0, 1, GRB.BINARY, "penaltyIndicator" + p.Description);
                m.Update();
                GRBLinExpr previousWeekValue = new GRBLinExpr();
                for (int bi = 0; bi < p.Batches.Count(); bi++)
                {
                    var b = p.Batches[bi];
                    // compare the minimal batch time (3 + 1 weeks) to the actual delivery time
                    var weekValue = new GRBLinExpr();
                    if (b.Compatibility == Batch.LineCompatibility.Line1)
                    {
                        var bIndex = bBc + batchesLine1.IndexOf(b);
                        for (int w = 0; w < maxWeek; w++)
                        {
                            weekValue += varBatchWeekLine1[bIndex, w] * w;
                        }
                    }
                    else if (b.Compatibility == Batch.LineCompatibility.Line2)
                    {
                        var bIndex = bBc + batchesLine2.IndexOf(b);
                        for (int w = 0; w < maxWeek; w++)
                        {
                            weekValue += varBatchWeekLine2[bIndex, w] * w;
                        }
                    }
                    else
                    {
                        var bIndex = batchesBothLines.IndexOf(b);
                        for (int w = 0; w < maxWeek; w++)
                        {
                            weekValue += varBatchWeekLine1[bIndex, w] * w;
                            weekValue += varBatchWeekLine2[bIndex, w] * w;
                        }
                    }

                    if (bi < p.Batches.Count() - 1)
                    {
                        // for positive difference add delay penalty, for negative difference add interest penalty
                        // var = max(0, x)
                        // var * delay
                        // (x - var) * interest
                        var plannedWeek = startWeekOfProject + bi * (weeksBuffer + 1);
                        var weekDiff    = weekValue - plannedWeek * totalBatchesOfProject[p].First(); // here we multiply the planned week with the (0/1)-allocation-indicator to avoid penalties when the project is not assigned (and therefore weekValue = 0)
                        m.AddConstr(varMaxedValue[bi] >= weekDiff);
                        m.AddConstr(varMaxedValue[bi] >= 0);
                        m.AddConstr(varMaxedValue[bi] <= weekDiff + maxWeek * (varDecisionVar[bi]));
                        m.AddConstr(varMaxedValue[bi] <= 0 + maxWeek * (1 - varDecisionVar[bi]));

                        double firstBatchImportance = bi == 0 ? 1 : 0.2; // mainly the first batch is important, the rest is meh

                        margin += varMaxedValue[bi] * delayPenaltyMultiplier * firstBatchImportance;
                        margin += (weekDiff - varMaxedValue[bi]) * interestPenaltyMultiplier * firstBatchImportance;
                    }

                    // constraint: batches of a project have to be in succession, i.e. batch2 can't come after batch3 chronologically
                    if (bi > 0)
                    {
                        m.AddConstr(weekValue - previousWeekValue >= 0);
                    }
                    previousWeekValue = weekValue;
                }
            }



            m.SetObjective(margin, GRB.MAXIMIZE);
            m.Update();

            m.Optimize();

            //env.Set(GRB.IntParam.IISMethod, 0); // makes IIS computation fast but potentially inaccurate
            //m.ComputeIIS();
            //m.Write("ilp.ilp");


            // build the solution from the optimization
            var sol = new Schedule();

            foreach (var p in scenario.Projects)
            {
                List <Schedule.BatchAllocation> allocatedBatches = new List <Schedule.BatchAllocation>();
                for (int bi = 0; bi < p.Batches.Count(); bi++)
                {
                    var b = p.Batches[bi];
                    // figure out on which week and which line the batch is allocated
                    int weekLine1 = 0;
                    int weekLine2 = 0;
                    if (b.Compatibility == Batch.LineCompatibility.Line1)
                    {
                        for (int w = 1; w <= maxWeek; w++)
                        {
                            weekLine1 += (int)varBatchWeekLine1[bBc + batchesLine1.IndexOf(b), w - 1].Get(GRB.DoubleAttr.X) * w;
                        }
                    }
                    else if (b.Compatibility == Batch.LineCompatibility.Line2)
                    {
                        for (int w = 1; w <= maxWeek; w++)
                        {
                            weekLine2 += (int)varBatchWeekLine2[bBc + batchesLine2.IndexOf(b), w - 1].Get(GRB.DoubleAttr.X) * w;
                        }
                    }
                    else
                    {
                        for (int w = 1; w <= maxWeek; w++)
                        {
                            weekLine1 += (int)varBatchWeekLine1[batchesBothLines.IndexOf(b), w - 1].Get(GRB.DoubleAttr.X) * w;
                            weekLine2 += (int)varBatchWeekLine2[batchesBothLines.IndexOf(b), w - 1].Get(GRB.DoubleAttr.X) * w;
                        }
                    }

                    if (weekLine1 > 0 && weekLine2 > 0)
                    {
                        throw new InvalidOperationException();
                    }

                    var alloc = Schedule.LineAllocation.None;
                    if (weekLine1 > 0)
                    {
                        alloc = Schedule.LineAllocation.Line1;
                    }
                    if (weekLine2 > 0)
                    {
                        alloc = Schedule.LineAllocation.Line2;
                    }

                    allocatedBatches.Add(new Schedule.BatchAllocation(b, alloc, earliestDate.AddDays(weekLine1 + weekLine2 - 1)));
                }
                sol.Add(new Schedule.ProjectAllocation(p, allocatedBatches));
            }

            return(sol);
        }
Esempio n. 18
0
        public override Schedule Generate(Scenario scenario, FilledBaseline fillerBaseline = null)
        {
            var projects = fillerBaseline == null ? scenario.Projects : fillerBaseline.Projects;
            // TODO: With fillerBaseline use ALL OF the projects

            var opportunities    = projects.Where(p => p is Opportunity).ToArray();
            var batches          = projects.SelectMany(p => p.Batches).ToArray();
            var batchesBothLines = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Both).ToArray();
            var batchesLine1     = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Line1).ToArray();
            var batchesLine2     = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Line2).ToArray();

            int       bBc          = batchesBothLines.Count();
            var       earliestDate = projects.Min(p => p.DeliveryDate);
            var       lastDate     = projects.Max(p => p.DeliveryDate.AddDays(7 * p.Batches.Count())).AddDays(7 * 3); // add an additional buffer of 3 weeks.
            double    maxDays      = (lastDate - earliestDate).Days;
            var       maxWeek      = maxDays / 7 + 1;
            const int weeksBuffer  = 3;


            var delayPenaltyMultiplier    = 0.01;            // 1% of revenue per week
            var interestPenaltyMultiplier = (0.045 / 52.14); // 4.5%  / 52.14 * (revenue – margin)


            var env = new GRBEnv();

            env.Set(GRB.IntParam.UpdateMode, 1);
            env.Set(GRB.IntParam.LogToConsole, 1);
            //env.Set(GRB.IntParam.Threads, 1);
            //env.Set(GRB.IntParam.NumericFocus, 3);
            //env.Set(GRB.DoubleParam.TimeLimit, 30);
            env.Set(GRB.DoubleParam.MIPGap, 0.06);


            //env.Set(GRB.IntParam.Presolve,2);
            //env.Set(GRB.IntParam.ScaleFlag, 2);
            //env.Set(GRB.IntParam.Method, 2);



            var m = new GRBModel(env);

            // decide: which batch is when, on which line
            var varBatchTimeLine1    = m.AddVars(bBc + batchesLine1.Count(), 0, maxDays, GRB.CONTINUOUS, "varBatchTimeLine1");
            var varBatchTimeLine2    = m.AddVars(bBc + batchesLine2.Count(), 0, maxDays, GRB.CONTINUOUS, "varBatchTimeLine2");
            var varOpportunityIsUsed = m.AddVars(opportunities.Count(), 0, 1, GRB.BINARY, "varOpportunityIsUsed");
            var varLineDecision      = m.AddVars(bBc, 0, 1, GRB.BINARY, "varLineDecision"); // 0 = Line1, 1 = Line2

            //m.Update();

            //TODO: Start heuristic
            //m.Update();
            //varMaxedValue[0].Set(GRB.DoubleAttr.Start, 5);

            // baseline constraint:
            // we want some opportunities to be used for sure

            /*if (baseline  != null)
             * {
             *  var mustWinOpportunitites = new List<Opportunity>();
             *  for (int oi = 0; oi < opportunities.Count(); oi++)
             *  {
             *      if (baseline.Projects.Contains(opportunities[oi]))
             *      {
             *          mustWinOpportunitites.Add((Opportunity)opportunities[oi]);
             *          m.AddConstr(varOpportunityIsUsed[opportunities.IndexOf(opportunities[oi])] == 1);
             *      }
             *  }
             *
             * }*/


            // line constraints:
            // if it's a fixed project though, then it must be allocated
            // for all batches which aren't assigned to a line yet, limit the allocation of yet unassigned batches of a project to one line
            // TODO: If project has e.g. line1 and both lines, limit both lines to line 1?
            for (int pi = 0; pi < projects.Count(); pi++)
            {
                var p = projects[pi];

                if (!p.Batches.Any(b => b.Compatibility == Batch.LineCompatibility.Both))
                {
                    continue;
                }

                int lineRestriction = -1;
                if (p.Batches.Any(b => b.Compatibility != Batch.LineCompatibility.Both))
                {
                    lineRestriction = p.Batches.First(b => b.Compatibility != Batch.LineCompatibility.Both).Compatibility == Batch.LineCompatibility.Line1 ? 0 : 1;
                }

                int i_prev = -1;
                for (int j = 0; j < p.Batches.Count(); j++)
                {
                    if (p.Batches[j].Compatibility != Batch.LineCompatibility.Both)
                    {
                        continue;
                    }

                    var i = batchesBothLines.IndexOf(p.Batches[j]);
                    if (lineRestriction == -1)
                    {
                        if (i_prev != -1)
                        {
                            m.AddConstr(varLineDecision[i] == varLineDecision[i_prev]);
                        }
                    }
                    else
                    {
                        // if there are other batches on this project which are already on a specific line, limit to the same line
                        m.AddConstr(varLineDecision[i] == lineRestriction);
                    }
                    i_prev = i;
                }
            }

            // scheduling formulation
            for (int l = 0; l < 2; l++)
            {
                var lineVars = l == 0 ? varBatchTimeLine1 : varBatchTimeLine2;
                var line     = batchesBothLines.ToList();
                line.AddRange(l == 0 ? batchesLine1 : batchesLine2);

                for (int bi1 = 0; bi1 < line.Count() - 1; bi1++)
                {
                    var batch1    = line[bi1];
                    var o1_exists = opportunities.FirstOrDefault(o => o.Batches.Contains(batch1));
                    var oi1       = o1_exists != null ? (1 - varOpportunityIsUsed[opportunities.IndexOf(o1_exists)]) : new GRBLinExpr();
                    var s1        = lineVars[bi1];
                    for (int bi2 = bi1 + 1; bi2 < line.Count(); bi2++)
                    {
                        var batch2    = line[bi2];
                        var o2_exists = opportunities.FirstOrDefault(o => o.Batches.Contains(batch2));
                        var oi2       = o2_exists != null ? (1 - varOpportunityIsUsed[opportunities.IndexOf(o2_exists)]) : new GRBLinExpr();
                        var s2        = lineVars[bi2];

                        // S1 - E2 >= 0 OR S2 - E1 >= 0
                        // IF both batches are used
                        var decisionVar             = m.AddVar(0, 1, GRB.BINARY, "schedulingORvar");
                        var opportunityNotUsedSlack = oi1 + oi2;
                        m.AddConstr(s1 - (s2 + batch2.UsedWorkHours / 24d) >= -maxDays * (decisionVar + opportunityNotUsedSlack)); //TODO: varLineDecisionSlack for both lines batches
                        m.AddConstr(s2 - (s1 + batch1.UsedWorkHours / 24d) >= -maxDays * (1 - decisionVar + opportunityNotUsedSlack));
                    }
                }
            }

            // Tbd: Only half of the batch slots of line 1 may be occupied. Sometimes existst as internal projects.
            // fill gap between 50% and internal projects, monthly resolution



            // Maximize the margin (including delay and interest penalties) and the workload
            // Only the delivery of the first batch is really important. The rest is less important.
            var margin     = new GRBLinExpr();
            var delays     = new List <GRBLinExpr>();
            var interests  = new List <GRBLinExpr>();
            var weekValues = new List <GRBLinExpr>();
            var weekDiffs  = new List <GRBLinExpr>();

            foreach (var p in projects)
            {
                // if the project is used add the margin
                if (p is Opportunity)
                {
                    margin += varOpportunityIsUsed[opportunities.IndexOf(p)] * p.Margin;
                }
                else
                {
                    margin += p.Margin;
                }

                // deduct the delay penalty for each batch.
                var startDayOfProject = (p.DeliveryDate - earliestDate).TotalDays;
                var varMaxedValue     = m.AddVars(p.Batches.Count(), 0, maxWeek, GRB.CONTINUOUS, "penaltyIndicator" + p.Description);
                var varDecisionVar    = m.AddVars(p.Batches.Count(), 0, 1, GRB.BINARY, "penaltyIndicator" + p.Description);
                //m.Update();
                GRBLinExpr previousWeekValue = new GRBLinExpr();
                for (int pbi = 0; pbi < p.Batches.Count(); pbi++)
                {
                    var b = p.Batches[pbi];
                    // compare the minimal batch time (3 + 1 weeks) to the actual delivery time

                    GRBLinExpr weekValue;
                    if (batchesLine1.Contains(b))
                    {
                        var bi = batchesLine1.IndexOf(b) + bBc;
                        weekValue = varBatchTimeLine1[bi] / 7d;
                    }
                    else if (batchesLine2.Contains(b))
                    {
                        var bi = batchesLine2.IndexOf(b) + bBc;
                        weekValue = varBatchTimeLine2[bi] / 7d;
                    }
                    else
                    {
                        var bi = batchesBothLines.IndexOf(b);
                        // create a new var
                        var bothLineWeekVar = m.AddVar(0, maxWeek, GRB.CONTINUOUS, "weekValueBothLines");
                        //m.Update();
                        m.AddConstr(bothLineWeekVar >= varBatchTimeLine1[bi] / 7d - (varLineDecision[bi]) * maxWeek);
                        m.AddConstr(bothLineWeekVar <= varBatchTimeLine1[bi] / 7d + (varLineDecision[bi]) * maxWeek);
                        m.AddConstr(bothLineWeekVar >= varBatchTimeLine2[bi] / 7d - (1 - varLineDecision[bi]) * maxWeek);
                        m.AddConstr(bothLineWeekVar <= varBatchTimeLine2[bi] / 7d + (1 - varLineDecision[bi]) * maxWeek);
                        weekValue = bothLineWeekVar;
                    }


                    if (true || pbi < p.Batches.Count() - 1)
                    {
                        // for positive difference add delay penalty, for negative difference add interest penalty
                        // x = opportunity used ? x : 0
                        // var = max(0, x)
                        // margin += var * delay
                        // margin += (x - var) * interest
                        var        plannedWeek = startDayOfProject / 7d + pbi * (weeksBuffer + 1);
                        GRBLinExpr weekDiff;
                        if (p is Opportunity)
                        {
                            var weekDiffIfUsed = m.AddVar(0, maxWeek, GRB.CONTINUOUS, "weekValueBothLines");
                            //m.Update();
                            var wD = weekValue - plannedWeek;
                            m.AddConstr(weekDiffIfUsed >= wD - (1 - varOpportunityIsUsed[opportunities.IndexOf(p)]) * maxWeek);
                            m.AddConstr(weekDiffIfUsed <= wD + (1 - varOpportunityIsUsed[opportunities.IndexOf(p)]) * maxWeek);
                            m.AddConstr(weekDiffIfUsed <= (varOpportunityIsUsed[opportunities.IndexOf(p)]) * maxWeek);
                            m.AddConstr(weekDiffIfUsed >= -(varOpportunityIsUsed[opportunities.IndexOf(p)]) * maxWeek);
                            weekDiff = weekDiffIfUsed;
                        }
                        else
                        {
                            weekDiff = weekValue - plannedWeek;
                        }

                        m.AddConstr(varMaxedValue[pbi] >= weekDiff);
                        m.AddConstr(varMaxedValue[pbi] >= 0);
                        m.AddConstr(varMaxedValue[pbi] <= weekDiff + maxWeek * (varDecisionVar[pbi]));
                        m.AddConstr(varMaxedValue[pbi] <= 0 + maxWeek * (1 - varDecisionVar[pbi]));


                        double firstBatchImportance = pbi == 0 ? 1 : 0.2; // mainly the first batch is important, the rest is meh
                        weekValues.Add(weekValue);
                        weekDiffs.Add(weekDiff);
                        double revenueBase = p.Revenue == 0 ? 999999999 : p.Revenue;
                        delays.Add(varMaxedValue[pbi] * delayPenaltyMultiplier * revenueBase * firstBatchImportance);
                        interests.Add(-(weekDiff - varMaxedValue[pbi]) * interestPenaltyMultiplier * revenueBase * firstBatchImportance);
                        margin -= delays.Last() + interests.Last();
                    }

                    // constraint: batches of a project have to be in succession, i.e. batch2 can't come after batch3 chronologically
                    if (pbi > 0)
                    {
                        m.AddConstr(weekValue - previousWeekValue >= 0);
                    }
                    previousWeekValue = weekValue;
                }
            }



            m.SetObjective(margin, GRB.MAXIMIZE);

            //m.Tune();
            //m.GetTuneResult(0);
            m.Optimize();

            //env.Set(GRB.IntParam.IISMethod, 0); // makes IIS computation fast but potentially inaccurate
            //m.ComputeIIS();
            //m.Write("ilp.ilp");


            // TODO: Max 5 weeks delay per project


            // build the solution from the optimization
            var    sol                = new Schedule();
            int    batchCount         = 0;
            double cumulatedDelays    = 0;
            double cumulatedInterests = 0;

            foreach (var p in projects)
            {
                List <Schedule.BatchAllocation> allocatedBatches = new List <Schedule.BatchAllocation>();
                var data = new List <double[]>();
                for (int bi = 0; bi < p.Batches.Count(); bi++)
                {
                    var b        = p.Batches[bi];
                    var delay    = delays[batchCount].Value;
                    var interest = interests[batchCount].Value;
                    cumulatedDelays    += delay;
                    cumulatedInterests += interest;
                    var weekValue = weekValues[batchCount].Value;
                    var weekDiff  = weekDiffs[batchCount].Value;
                    // figure out on which week and which line the batch is allocated
                    double dayLine1 = 0;
                    double dayLine2 = 0;
                    if (b.Compatibility == Batch.LineCompatibility.Line1)
                    {
                        dayLine1 = varBatchTimeLine1[bBc + batchesLine1.IndexOf(b)].Get(GRB.DoubleAttr.X);
                    }
                    else if (b.Compatibility == Batch.LineCompatibility.Line2)
                    {
                        dayLine2 = varBatchTimeLine2[bBc + batchesLine2.IndexOf(b)].Get(GRB.DoubleAttr.X);
                    }
                    else
                    {
                        var bbi          = batchesBothLines.IndexOf(b);
                        var lineDecision = varLineDecision[bbi].Get(GRB.DoubleAttr.X);
                        dayLine1 = varBatchTimeLine1[bbi].Get(GRB.DoubleAttr.X) * (1 - lineDecision);
                        dayLine2 = varBatchTimeLine2[bbi].Get(GRB.DoubleAttr.X) * lineDecision;
                    }

                    data.Add(new double[] { delay, interest, weekValue, weekDiff, dayLine1 + dayLine2 });

                    if (dayLine1 > 0 && dayLine2 > 0)
                    {
                        throw new InvalidOperationException();
                    }

                    var alloc = Schedule.LineAllocation.None;
                    if (p is FixedProject || (p is Opportunity && varOpportunityIsUsed[opportunities.IndexOf(p)].Get(GRB.DoubleAttr.X) > 0.5))
                    {
                        if (b.Compatibility == Batch.LineCompatibility.Both)
                        {
                            alloc = varLineDecision[batchesBothLines.IndexOf(b)].Get(GRB.DoubleAttr.X) > 0.5 ? Schedule.LineAllocation.Line2 : Schedule.LineAllocation.Line1;
                        }
                        else
                        {
                            alloc = b.Compatibility == Batch.LineCompatibility.Line1 ? Schedule.LineAllocation.Line1 : Schedule.LineAllocation.Line2;
                        }
                    }
                    //if (dayLine1 > 0) alloc = Solution.LineAllocation.Line1;
                    //if (dayLine2 > 0) alloc = Solution.LineAllocation.Line2;

                    allocatedBatches.Add(new Schedule.BatchAllocation(b, alloc, earliestDate.AddDays(dayLine1 + dayLine2)));
                    batchCount++;
                }
                sol.Add(new Schedule.ProjectAllocation(p, allocatedBatches));
            }

            return(sol);
        }
        void BuildModel_RestrMaster()
        {
            _env = new GRBEnv("SolutionLog.log");
            _env.Set(GRB.DoubleParam.MIPGap, 0.0);
            _env.Set(GRB.DoubleParam.TimeLimit, 500);
            _grbModel = new GRBModel(_env);

            //决策变量
            foreach (Node n in Data.NodeSet)
            {
                n.Result_GenerateFlow = _grbModel.AddVar(0.0, M, 0.0, GRB.CONTINUOUS, "g_" + n.ID);
            }

            foreach (Dictionary <Node, int> scheme in SchemeSet)
            {
                _grbModel.AddVar(0.0, 1.0, 0.0, GRB.CONTINUOUS, "k_" + SchemeSet.IndexOf(scheme));
            }

            foreach (Arc a in Data.ArcSet)
            {
                a.Result_FlowF = _grbModel.AddVar(0.0, M, 0.0, GRB.CONTINUOUS, "fF_" + a.FromNode.ID + "_" + a.ToNode.ID);
                a.Result_FlowR = _grbModel.AddVar(0.0, M, 0.0, GRB.CONTINUOUS, "fR_" + a.FromNode.ID + "_" + a.ToNode.ID);
            }
            _grbModel.Update();

            //目标函数
            GRBLinExpr expr1 = 0;

            foreach (Dictionary <Node, int> scheme in SchemeSet)
            {
                foreach (Node n in Data.NodeSet)
                {
                    expr1 += scheme[n] * Data.ServerInstalationFee * _grbModel.GetVarByName("k_" + SchemeSet.IndexOf(scheme));
                }
            }
            GRBLinExpr expr2 = 0;

            foreach (Arc a in Data.ArcSet)
            {
                expr2 += (a.Result_FlowF + a.Result_FlowR) * Data.FlowFeePerUnit;
            }

            _grbModel.SetObjective(expr1 + expr2, GRB.MINIMIZE);


            //约束条件

            foreach (Node n in Data.NodeSet)
            {
                GRBLinExpr expr = 0;
                foreach (Dictionary <Node, int> scheme in SchemeSet)
                {
                    expr += scheme[n] * _grbModel.GetVarByName("k_" + SchemeSet.IndexOf(scheme)) * M;
                }
                _grbModel.AddConstr(expr - n.Result_GenerateFlow >= 0, "ct1_" + n.ID);
            }

            foreach (Node n in Data.NodeSet)
            {
                GRBLinExpr sum1 = 0;
                GRBLinExpr sum2 = 0;
                GRBLinExpr sum3 = 0;
                GRBLinExpr sum4 = 0;
                foreach (Arc a in n.ArcSet)
                {
                    if (a.ToNode == n)//进
                    {
                        sum1 += a.Result_FlowF;
                        sum3 += a.Result_FlowR;
                    }
                    else//出
                    {
                        sum2 += a.Result_FlowR;
                        sum4 += a.Result_FlowF;
                    }
                }
                _grbModel.AddConstr(n.Result_GenerateFlow + sum1 + sum2 == n.Demand + sum3 + sum4, "ct2_" + n.ID);
            }
            foreach (Arc a in Data.ArcSet)
            {
                _grbModel.AddConstr(a.Result_FlowF + a.Result_FlowR <= a.Capacity, "ct3_" + a.FromNode.ID + "_" + a.ToNode.ID);
            }

            GRBLinExpr exprCtr = 0;

            foreach (Dictionary <Node, int> scheme in SchemeSet)
            {
                exprCtr += _grbModel.GetVarByName("k_" + SchemeSet.IndexOf(scheme));
            }
            _grbModel.AddConstr(exprCtr == 1, "ct4");
        }
        void BuildModel_SubProblem2()
        {
            _env = new GRBEnv("SolutionLog.log");
            _env.Set(GRB.DoubleParam.MIPGap, 0.0);
            _env.Set(GRB.DoubleParam.TimeLimit, 500);
            _grbModel = new GRBModel(_env);

            //决策变量
            foreach (Node n in Data.NodeSet)
            {
                n.Result_GenerateFlow = _grbModel.AddVar(0.0, GRB.INFINITY, 0.0, GRB.CONTINUOUS, "g_" + n.ID);
            }
            foreach (Arc a in Data.ArcSet)
            {
                a.Result_FlowF = _grbModel.AddVar(0.0, GRB.INFINITY, 0.0, GRB.CONTINUOUS, "fF_" + a.FromNode.ID + "_" + a.ToNode.ID);
                a.Result_FlowR = _grbModel.AddVar(0.0, GRB.INFINITY, 0.0, GRB.CONTINUOUS, "fR_" + a.FromNode.ID + "_" + a.ToNode.ID);
            }
            _grbModel.Update();

            //目标函数
            GRBLinExpr expr1 = 0;

            foreach (Node n in Data.NodeSet)
            {
                expr1 += n.Result_GenerateFlow * multiplier_p[n];
            }
            GRBLinExpr expr2 = 0;

            foreach (Arc a in Data.ArcSet)
            {
                expr2 += (a.Result_FlowF + a.Result_FlowR) * Data.FlowFeePerUnit;
            }

            _grbModel.SetObjective(expr1 + expr2, GRB.MINIMIZE);


            //约束条件
            foreach (Node n in Data.NodeSet)
            {
                GRBLinExpr sum1 = 0;
                GRBLinExpr sum2 = 0;
                GRBLinExpr sum3 = 0;
                GRBLinExpr sum4 = 0;
                foreach (Arc a in n.ArcSet)
                {
                    if (a.ToNode == n)//进
                    {
                        sum1 += a.Result_FlowF;
                        sum3 += a.Result_FlowR;
                    }
                    else//出
                    {
                        sum2 += a.Result_FlowR;
                        sum4 += a.Result_FlowF;
                    }
                }
                _grbModel.AddConstr(n.Result_GenerateFlow + sum1 + sum2 == n.Demand + sum3 + sum4, "ct2_" + n.ID);
            }
            foreach (Arc a in Data.ArcSet)
            {
                _grbModel.AddConstr(a.Result_FlowF + a.Result_FlowR <= a.Capacity, "ct3_" + a.FromNode.ID + "_" + a.ToNode.ID);
            }
        }