8.8. C# API

本章节提供MindOpt的C# API手册,内容见下文。

8.8.16. Examples

8.8.16.1. 定制食谱

using Mindopt;

namespace Example
{
    public class ExampleDiet
    {
        public static void Main(string[] args)
        {
            MDOEnv env = new MDOEnv();
            MDOModel model = new MDOModel(env);
            model.Set(MDO.StringAttr.ModelName, "diet");

            Dictionary<string, double[]> requirements = new()
            {
                {"Calories",    new double[]{ 2000, MDO.INFINITY }},
                {"Carbohydrates",         new double[]{ 350, 375 }},
                {"Protein",       new double[]{ 55, MDO.INFINITY }},
                {"VitA",         new double[]{ 100, MDO.INFINITY }},
                {"VitC",         new double[]{ 100, MDO.INFINITY }},
                {"Calcium",      new double[]{ 100, MDO.INFINITY }},
                {"Iron",         new double[]{ 100, MDO.INFINITY }},
                {"Volume",        new double[]{ -MDO.INFINITY,75 }}
            };

            Dictionary<string, double[]> foods = new()
            {
                {"Cheeseburger",    new double[]{ 0, MDO.INFINITY, 1.84 }},
                {"HamSandwich",     new double[]{ 0, MDO.INFINITY, 2.19 }},
                {"Hamburger",       new double[]{ 0, MDO.INFINITY, 1.84 }},
                {"FishSandwich",    new double[]{ 0, MDO.INFINITY, 1.44 }},
                {"ChickenSandwich", new double[]{ 0, MDO.INFINITY, 2.29 }},
                {"Fries",           new double[]{ 0, MDO.INFINITY, 0.77 }},
                {"SausageBiscuit",  new double[]{ 0, MDO.INFINITY, 1.29 }},
                {"LowfatMilk",      new double[]{ 0, MDO.INFINITY, 0.60 }},
                {"OrangeJuice",     new double[]{ 0, MDO.INFINITY, 0.72 }}
            };

            double[,] values = new double[,]
            {
                { 510.0, 34.0, 28.0, 15.0,   6.0, 30.0, 20.0,  4.0 },
                { 370.0, 35.0, 24.0, 15.0,  10.0, 20.0, 20.0,  7.5 },
                { 500.0, 42.0, 25.0,  6.0,   2.0, 25.0, 20.0,  3.5 },
                { 370.0, 38.0, 14.0,  2.0,   0.0, 15.0, 10.0,  5.0 },
                { 400.0, 42.0, 31.0,  8.0,  15.0, 15.0,  8.0,  7.3 },
                { 220.0, 26.0,  3.0,  0.0,  15.0,  0.0,  2.0,  2.6 },
                { 345.0, 27.0, 15.0,  4.0,   0.0, 20.0, 15.0,  4.1 },
                { 110.0, 12.0,  9.0, 10.0, 120.0, 30.0,  0.0,  8.0 },
                {  80.0, 20.0,  1.0,  2.0,   4.0,  2.0,  2.0, 12.0 }
            };

            string[] nutritionNames = requirements.Keys.Cast<string>().ToArray();
            int numNutrition = nutritionNames.Length;
            string[] foodNames = foods.Keys.Cast<string>().ToArray();
            int numFood = foodNames.Length;

            Dictionary<string, Dictionary<string, double>> reqValues = new();

            for (int i = 0; i < foodNames.Length; i++)
            {
                reqValues[foodNames[i]] = new Dictionary<string, double>();
                for (int j = 0; j < nutritionNames.Length; j++)
                    reqValues[foodNames[i]][nutritionNames[j]] = values[i, j];
            }

            try
            {
                MDOVar[] foodVars = new MDOVar[numFood];
                for (int i = 0; i < numFood; ++i)
                {
                    double[] foodData = foods[foodNames[i]];
                    foodVars[i] = model.AddVar(foodData[0], foodData[1], 0, MDO.CONTINUOUS, foodNames[i]);
                }

                // 添加约束
                for (int i = 0; i < numNutrition; i++)
                {
                    MDOLinExpr lin = new MDOLinExpr();
                    string nutri = nutritionNames[i];
                    for (int j = 0; j < numFood; j++)
                    {
                        string food = foodNames[j];
                        lin.AddTerm(reqValues[food][nutri], foodVars[j]);
                    }

                    model.AddRange(lin, requirements[nutritionNames[i]][0], requirements[nutritionNames[i]][1], nutritionNames[i]);
                }

                // 添加目标函数
                MDOLinExpr linExpr = new MDOLinExpr();
                for (int i = 0; i < numFood; i++)
                    linExpr.AddTerm(foods[foodNames[i]][2], foodVars[i]);

                model.SetObjective(linExpr, MDO.MINIMIZE);

                model.Optimize();

                model.Write("ExmapleDiet.mps");
                // 打印结果
                foreach (MDOVar foodVar in foodVars)
                    Console.WriteLine($"You should buy {foodVar.Get(MDO.DoubleAttr.X)} unit of {foodVar.Get(MDO.StringAttr.VarName)}");

            }
            catch (MDOException e)
            {
                Console.WriteLine(e.Message);
            }
            finally
            {
                model.Dispose();
                env.Dispose();
            }
        }
    }
}

8.8.16.2. 设施选址

using Mindopt;

namespace Example
{
    public class ExampleFacility
    {
        public static void Main(string[] args)
        {
            MDOEnv env = new MDOEnv();
            MDOModel model = new MDOModel(env);
            model.Set(MDO.StringAttr.ModelName, "Facility");

            Dictionary<Tuple<double, double>, int> marketInfo = new()
            {
                {Tuple.Create(0.0, 1.7), 100},
                {Tuple.Create(1.4, 2.9), 200},
            };

            Dictionary<Tuple<int, int>, double> facilitiesInfo = new()
            {
                {Tuple.Create(0, 1), 3.0},
                {Tuple.Create(0, 2), 1.0},
                {Tuple.Create(1, 0), 1.5},
                {Tuple.Create(1, 1), 1.3},
                {Tuple.Create(1, 2), 1.8},
                {Tuple.Create(2, 0), 1.6},
                {Tuple.Create(2, 1), 1.1},
                {Tuple.Create(2, 2), 1.9}
            };

            double transportFeePerM = 1.23;

            try
            {
                int i = 0;
                int j = 0;

                MDOVar[] xVars = model.AddVars(facilitiesInfo.Count, MDO.BINARY);
                MDOVar[,] yVars = new MDOVar[marketInfo.Count, facilitiesInfo.Count];
                for (i = 0; i < marketInfo.Count; i++)
                {
                    for (j = 0; j < facilitiesInfo.Count; j++)
                        yVars[i, j] = model.AddVar(0, MDO.INFINITY, 0, MDO.CONTINUOUS, $"{i}{j}");
                }

                i = 0;

                foreach (KeyValuePair<Tuple<double, double>, int> marketPair in marketInfo)
                {
                    MDOLinExpr linExpr = new MDOLinExpr();

                    for (j = 0; j < facilitiesInfo.Count; j++)
                    {
                        linExpr.AddTerm(1, yVars[i, j]);
                        MDOLinExpr lhe = new MDOLinExpr();
                        lhe.AddTerm(1.0 / marketPair.Value, yVars[i, j]);
                        model.AddConstr(lhe, MDO.LESS_EQUAL, xVars[j], $"is_built[{i}, {j}]");
                    }
                    model.AddConstr(linExpr, MDO.EQUAL, marketPair.Value, $"is_satisify[{i}, {j}]");
                    i++;
                }

                MDOLinExpr objective = new MDOLinExpr();

                j = 0;
                foreach (KeyValuePair<Tuple<int, int>, double> facPair in facilitiesInfo)
                {
                    objective.AddTerm(facPair.Value, xVars[j]);
                    i = 0;
                    foreach (Tuple<double, double> marketPos in marketInfo.Keys)
                    {
                        objective.AddTerm(calcTransFee(marketPos, facPair.Key, transportFeePerM), xVars[j]);
                        i++;
                    }
                    j++;
                }

                model.SetObjective(objective, MDO.MINIMIZE);
                model.Optimize();
                model.Write("ExampleFacility.mps");

                i = 0;
                foreach (Tuple<int, int> pos in facilitiesInfo.Keys)
                {
                    MDOVar x = xVars[i];
                    if (x.Get(MDO.DoubleAttr.X) == 1) {
                        Console.Write($"The No.{i} warehouse should be built at ");
                        Console.WriteLine($"({pos.Item1}, {pos.Item2})");
                    }
                    i++;
                }
                Console.WriteLine($"Trnasport fee is: {objective.Value} ");
            }
            catch (MDOException e)
            {
                Console.WriteLine(e.Message);
            }
            finally
            {
                model.Dispose();
                env.Dispose();
            }
        }

        private static double calcTransFee(Tuple<double, double> pos1, Tuple<int, int> pos2, double unitPrice)
        {
            double x1 = pos1.Item1 - pos2.Item1;
            double x2 = pos1.Item2 - pos2.Item2;
            return (x1 * x1 + x2 * x2) * unitPrice;
        }
    }
}

8.8.16.3. 人力分配

using Mindopt;

namespace Example
{
    public class ExampleWorkforce
    {
        public static void Main(string[] args)
        {
            // 创建MindOpt模型
            MDOEnv env = new MDOEnv();
            MDOModel model = new MDOModel(env);

            // 每天需要的工日数目
            string[] dayName = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
            int[] workersPerDay = {  3, 1, 4, 2, 1, 3, 3 };

            // 对应工人的工资
            Dictionary<string, int> workers = new()
            {
                {"Xiaoming", 13},
                {"Huahua",   10},
                {"HongHong", 11},
                {"Dahua",     8},
                {"Lihua",     9},
                {"Niuniu",   14},
                {"Gouzi",    14}
            };

            List<string> workers_name = new List<string>(workers.Keys);

            // 定义可用集合
            List<Tuple<string, string>> availability = new()
            {
                Tuple.Create( "Xiaoming",     "Tuesday" ),
                Tuple.Create( "Xiaoming",   "Wednesday" ),
                Tuple.Create( "Xiaoming",      "Friday" ),
                Tuple.Create( "Xiaoming",      "Sunday" ),
                Tuple.Create( "Huahua",        "Monday" ),
                Tuple.Create( "Huahua",       "Tuesday" ),
                Tuple.Create( "Huahua",        "Friday" ),
                Tuple.Create( "Huahua",      "Saturday" ),
                Tuple.Create( "HongHong",   "Wednesday" ),
                Tuple.Create( "HongHong",    "Thursday" ),
                Tuple.Create( "HongHong",      "Friday" ),
                Tuple.Create( "HongHong",      "Sunday" ),
                Tuple.Create( "Dahua",        "Tuesday" ),
                Tuple.Create( "Dahua",      "Wednesday" ),
                Tuple.Create( "Dahua",         "Friday" ),
                Tuple.Create( "Dahua",       "Saturday" ),
                Tuple.Create( "Lihua",         "Monday" ),
                Tuple.Create( "Lihua",        "Tuesday" ),
                Tuple.Create( "Lihua",      "Wednesday" ),
                Tuple.Create( "Lihua",       "Thursday" ),
                Tuple.Create( "Lihua",         "Friday" ),
                Tuple.Create( "Lihua",         "Sunday" ),
                Tuple.Create( "Niuniu",        "Monday" ),
                Tuple.Create( "Niuniu",       "Tuesday" ),
                Tuple.Create( "Niuniu",     "Wednesday" ),
                Tuple.Create( "Niuniu",      "Saturday" ),
                Tuple.Create( "Gouzi",         "Monday" ),
                Tuple.Create( "Gouzi",        "Tuesday" ),
                Tuple.Create( "Gouzi",      "Wednesday" ),
                Tuple.Create( "Gouzi",         "Friday" ),
                Tuple.Create( "Gouzi",       "Saturday" ),
                Tuple.Create( "Gouzi",         "Sunday" )
            };

            try
            {
                MDOVar[] x = new MDOVar[availability.Count];
                for (int i = 0; i < x.Length; i++)
                {
                    Tuple<string, string> workerDay = availability[i];
                    x[i] = model.AddVar(0, 1, 0, MDO.BINARY, $"sched({workerDay.Item1}, {workerDay.Item2})");
                }

                Dictionary<string, MDOLinExpr> dayCount = new();
                foreach (string day in dayName) dayCount[day] = new MDOLinExpr();

                for (int i = 0; i < availability.Count; i++)
                {
                    MDOLinExpr expr = new MDOLinExpr();
                    Tuple<string, string> workerDay = availability[i];
                    dayCount[workerDay.Item2].AddTerm(1, x[i]);
                }

                for (int i = 0; i < dayName.Length; i++)
                {
                    MDOLinExpr expr = dayCount[dayName[i]];
                    model.AddConstr(expr, MDO.EQUAL, workersPerDay[i], "c1_" + availability[i].Item2);
                }

                MDOLinExpr obj = new MDOLinExpr();
                for (int i = 0; i < availability.Count; i++)
                    obj.AddTerm(workers[availability[i].Item1], x[i]);

                model.SetObjective(obj, MDO.MINIMIZE);
                model.Optimize();

                for (int i = 0; i < availability.Count; i++)
                {
                    if (x[i].Get(MDO.DoubleAttr.X) > 0)
                        Console.WriteLine($"{availability[i].Item1} should work at {availability[i].Item2}");
                }
                Console.WriteLine($"The total cost is { + model.Get(MDO.DoubleAttr.ObjVal)}");
            }
            catch (MDOException e)
            {
                Console.WriteLine(e.Message);
            }
            finally
            {
                model.Dispose();
                env.Dispose();
            }
        }
    }
}