Example #1
0
        /// <summary>
        /// Solves this model.
        /// </summary>
        public void Solve()
        {
            // Init
            LogLine("Solving ...");
            DateTime    before            = DateTime.Now;
            LinearModel model             = new LinearModel(SolverType.Gurobi, (string msg) => { Log(msg); });
            VariableCollection <int> xVar = new VariableCollection <int>(model, VariableType.Continuous, 0, double.PositiveInfinity, (int j) => { return("x" + j.ToString()); });

            // Modify some control parameters
            foreach (var param in _solverArgs)
            {
                model.SetParam(param.Key, param.Value);
            }

            // Prepare non-zero indices
            Dictionary <int, List <int> > nnzs = _coefficientMatrix.Keys.GroupBy(k => (int)k[0]).ToDictionary(k => k.Key, v => v.Select(e => (int)e[1]).ToList());

            // Add objective
            model.SetObjective(
                LinearExpression.Sum(
                    Enumerable.Range(0, N).Select(j => _objCoeffcientVector[j]),
                    Enumerable.Range(0, N).Select(j => xVar[j])),
                OptimizationSense.Minimize);
            // Add constraint
            foreach (var i in nnzs.Keys)
            {
                model.AddConstr(LinearExpression.Sum(
                                    nnzs[i].Select(j => _coefficientMatrix[i, j]),
                                    nnzs[i].Select(j => xVar[j]))
                                == _rhsVector[i],
                                "Con" + i);
            }
            // TODO remove debug
            model.Update();
            model.ExportMPS("mdp.mps");
            // Solve
            model.Optimize();
            TimeSpan solutionTime = DateTime.Now - before;

            // Get solution
            if (model.HasSolution())
            {
                LogLine("Solution found!");
                _solutionVector   = Enumerable.Range(0, N).Select(j => xVar[j].Value).ToList();
                _solutionValue    = model.GetObjectiveValue();
                SolutionAvailable = true;
            }
            else
            {
                LogLine("No solution!");
            }
            // Log performance
            LogPerformance(before, Filename, solutionTime, SolutionAvailable, _solutionValue, _solverArgs.Select(a => a.Key + "=" + a.Value));
        }
Example #2
0
        private void AnalyzeServiceUnits()
        {
            // Keep track of count
            int overallCount = _serviceUnits.Count * _config.GroupsIndividuals.Count;
            int counter      = 0;

            // --> Solve all
            _config.LogLine("Analyzing all service units within all given groups - " + overallCount + " to go!");
            // Iterate all groups (use a dummy group if no grouping has to be done - this should work ok with the interior part of the loops)
            foreach (var groupIdents in _config.GroupsIndividuals)
            {
                // Investigate all service units for this group within the given scenario
                foreach (var serviceUnitInFocus in _serviceUnits)
                {
                    // Log
                    _config.Log((++counter) + "/" + overallCount + ": " + serviceUnitInFocus.Ident + "/" + (groupIdents.Any() ? string.Join(",", groupIdents.Select(i => i.Item2)) : "Overall") + " (SU/group)");
                    // Init
                    LinearModel model = new LinearModel(_config.SolverChoice, null /* Disable output flood for now, use following to enable: (string s) => { _config.Log(s); } */);
                    // --> Init variables
                    Variable efficiencyRating = new Variable(model, VariableType.Continuous, double.NegativeInfinity, double.PositiveInfinity, "EfficiencyRating");
                    VariableCollection <ServiceUnit> weights = new VariableCollection <ServiceUnit>(model, VariableType.Continuous, 0, double.PositiveInfinity, (ServiceUnit u) => { return(u.Ident); });

                    // --> Build model
                    // Add objective
                    if (_config.InputOriented)
                    {
                        model.SetObjective(efficiencyRating + 0, OptimizationSense.Minimize);
                    }
                    else
                    {
                        model.SetObjective(efficiencyRating + 0, OptimizationSense.Maximize);
                    }
                    // Add input constraints
                    if (_config.Inputs.Any())
                    {
                        foreach (var inputEntry in _config.Inputs)
                        {
                            // Shift and flip (if required) all values
                            Dictionary <ServiceUnit, double> inputValues = _serviceUnits.ToDictionary(
                                k => k,
                                s => s.GetValue(inputEntry.Item1, (FootprintDatapoint f) => { return(groupIdents.All(g => g.Item2 == f[g.Item1].ToString())); }));
                            // In case of loss values, convert them
                            if (inputEntry.Item2 == InputType.Hindrance)
                            {
                                switch (inputEntry.Item1)
                                {
                                case FootprintDatapoint.FootPrintEntry.SKUs:
                                    // Simply inverse them
                                    foreach (var serviceUnit in inputValues.Keys.ToList())
                                    {
                                        inputValues[serviceUnit] = 1.0 / inputValues[serviceUnit];
                                    }
                                    break;

                                default:
                                    // Simply flip them (next step will convert the numbers to positive ones again)
                                    foreach (var serviceUnit in inputValues.Keys.ToList())
                                    {
                                        inputValues[serviceUnit] *= -1;
                                    }
                                    break;
                                }
                            }
                            // If there is a negative value, shift all values to positive ones
                            double minOutputValue = inputValues.Min(v => v.Value);
                            if (minOutputValue < 0)
                            {
                                foreach (var serviceUnit in inputValues.Keys.ToList())
                                {
                                    inputValues[serviceUnit] += Math.Abs(minOutputValue);
                                }
                            }
                            // Add constraint
                            if (_config.InputOriented)
                            {
                                model.AddConstr(
                                    // Sum of all other weighted inputs
                                    LinearExpression.Sum(_serviceUnits.Select(s => inputValues[s]), _serviceUnits.Select(s => weights[s])) <=
                                    // Shall be smaller than the weighted input of the service unit in focus
                                    efficiencyRating * inputValues[serviceUnitInFocus], "Input" + inputEntry);
                            }
                            else
                            {
                                model.AddConstr(
                                    // Sum of all other weighted inputs
                                    LinearExpression.Sum(_serviceUnits.Select(s => inputValues[s]), _serviceUnits.Select(s => weights[s])) <=
                                    // Shall be smaller than the weighted input of the service unit in focus
                                    inputValues[serviceUnitInFocus], "Input" + inputEntry);
                            }
                        }
                    }
                    else
                    {
                        // Add constant uniform inputs for all service units
                        if (_config.InputOriented)
                        {
                            model.AddConstr(
                                // Sum of all other weighted inputs
                                LinearExpression.Sum(_serviceUnits.Select(s => 1.0), _serviceUnits.Select(s => weights[s])) <=
                                // Shall be smaller than the weighted input of the service unit in focus
                                efficiencyRating * 1.0, "InputConstant");
                        }
                        else
                        {
                            model.AddConstr(
                                // Sum of all other weighted inputs
                                LinearExpression.Sum(_serviceUnits.Select(s => 1.0), _serviceUnits.Select(s => weights[s])) <=
                                // Shall be smaller than the weighted input of the service unit in focus
                                1.0, "InputConstant");
                        }
                    }
                    // Add output constraints
                    if (_config.Outputs.Any())
                    {
                        // Add output constraint using the actual entry
                        foreach (var outputEntry in _config.Outputs)
                        {
                            // Shift and flip (if required) all values
                            Dictionary <ServiceUnit, double> outputValues = _serviceUnits.ToDictionary(
                                k => k,
                                s => s.GetValue(outputEntry.Item1, (FootprintDatapoint f) => { return(groupIdents.All(g => g.Item2 == f[g.Item1].ToString())); }));
                            // Store performance for this measure
                            foreach (var serviceUnit in _serviceUnits)
                            {
                                _serviceUnitOutputMeasures[serviceUnit, outputEntry.Item1] = outputValues[serviceUnit];
                            }
                            // In case of loss values, convert them
                            if (outputEntry.Item2 == OutputType.Loss)
                            {
                                switch (outputEntry.Item1)
                                {
                                case FootprintDatapoint.FootPrintEntry.OrderTurnoverTimeAvg:
                                case FootprintDatapoint.FootPrintEntry.OrderTurnoverTimeMed:
                                case FootprintDatapoint.FootPrintEntry.OrderTurnoverTimeLQ:
                                case FootprintDatapoint.FootPrintEntry.OrderTurnoverTimeUQ:
                                case FootprintDatapoint.FootPrintEntry.OrderThroughputTimeAvg:
                                case FootprintDatapoint.FootPrintEntry.OrderThroughputTimeMed:
                                case FootprintDatapoint.FootPrintEntry.OrderThroughputTimeLQ:
                                case FootprintDatapoint.FootPrintEntry.OrderThroughputTimeUQ:
                                case FootprintDatapoint.FootPrintEntry.BundleTurnoverTimeAvg:
                                case FootprintDatapoint.FootPrintEntry.BundleTurnoverTimeMed:
                                case FootprintDatapoint.FootPrintEntry.BundleTurnoverTimeLQ:
                                case FootprintDatapoint.FootPrintEntry.BundleTurnoverTimeUQ:
                                case FootprintDatapoint.FootPrintEntry.BundleThroughputTimeAvg:
                                case FootprintDatapoint.FootPrintEntry.BundleThroughputTimeMed:
                                case FootprintDatapoint.FootPrintEntry.BundleThroughputTimeLQ:
                                case FootprintDatapoint.FootPrintEntry.BundleThroughputTimeUQ:
                                    // Simply inverse them
                                    foreach (var serviceUnit in outputValues.Keys.ToList())
                                    {
                                        outputValues[serviceUnit] = 1.0 / outputValues[serviceUnit];
                                    }
                                    break;

                                case FootprintDatapoint.FootPrintEntry.OSIdleTimeAvg:
                                case FootprintDatapoint.FootPrintEntry.OSIdleTimeMed:
                                case FootprintDatapoint.FootPrintEntry.OSIdleTimeLQ:
                                case FootprintDatapoint.FootPrintEntry.OSIdleTimeUQ:
                                case FootprintDatapoint.FootPrintEntry.ISIdleTimeAvg:
                                case FootprintDatapoint.FootPrintEntry.ISIdleTimeMed:
                                case FootprintDatapoint.FootPrintEntry.ISIdleTimeLQ:
                                case FootprintDatapoint.FootPrintEntry.ISIdleTimeUQ:
                                case FootprintDatapoint.FootPrintEntry.OSDownTimeAvg:
                                case FootprintDatapoint.FootPrintEntry.OSDownTimeMed:
                                case FootprintDatapoint.FootPrintEntry.OSDownTimeLQ:
                                case FootprintDatapoint.FootPrintEntry.OSDownTimeUQ:
                                case FootprintDatapoint.FootPrintEntry.ISDownTimeAvg:
                                case FootprintDatapoint.FootPrintEntry.ISDownTimeMed:
                                case FootprintDatapoint.FootPrintEntry.ISDownTimeLQ:
                                case FootprintDatapoint.FootPrintEntry.ISDownTimeUQ:
                                case FootprintDatapoint.FootPrintEntry.LateOrdersFractional:
                                    // As these should be values between 0.0 and 1.0 just flip them within the range
                                {
                                    if (outputValues.Values.Any(v => v < 0 || v > 1))
                                    {
                                        throw new ArgumentException("Expected values to be within the range [0,1], but found one out of range!");
                                    }
                                    foreach (var serviceUnit in outputValues.Keys.ToList())
                                    {
                                        outputValues[serviceUnit] = 1.0 - outputValues[serviceUnit];
                                    }
                                }
                                break;

                                case FootprintDatapoint.FootPrintEntry.TimingDecisionsOverall:
                                case FootprintDatapoint.FootPrintEntry.TimingPathPlanningAvg:
                                case FootprintDatapoint.FootPrintEntry.TimingPathPlanningOverall:
                                case FootprintDatapoint.FootPrintEntry.TimingPathPlanningCount:
                                case FootprintDatapoint.FootPrintEntry.TimingTaskAllocationAvg:
                                case FootprintDatapoint.FootPrintEntry.TimingTaskAllocationOverall:
                                case FootprintDatapoint.FootPrintEntry.TimingTaskAllocationCount:
                                case FootprintDatapoint.FootPrintEntry.TimingItemStorageAvg:
                                case FootprintDatapoint.FootPrintEntry.TimingItemStorageOverall:
                                case FootprintDatapoint.FootPrintEntry.TimingItemStorageCount:
                                case FootprintDatapoint.FootPrintEntry.TimingPodStorageAvg:
                                case FootprintDatapoint.FootPrintEntry.TimingPodStorageOverall:
                                case FootprintDatapoint.FootPrintEntry.TimingPodStorageCount:
                                case FootprintDatapoint.FootPrintEntry.TimingRepositioningAvg:
                                case FootprintDatapoint.FootPrintEntry.TimingRepositioningOverall:
                                case FootprintDatapoint.FootPrintEntry.TimingRepositioningCount:
                                case FootprintDatapoint.FootPrintEntry.TimingReplenishmentBatchingAvg:
                                case FootprintDatapoint.FootPrintEntry.TimingReplenishmentBatchingOverall:
                                case FootprintDatapoint.FootPrintEntry.TimingReplenishmentBatchingCount:
                                case FootprintDatapoint.FootPrintEntry.TimingOrderBatchingAvg:
                                case FootprintDatapoint.FootPrintEntry.TimingOrderBatchingOverall:
                                case FootprintDatapoint.FootPrintEntry.TimingOrderBatchingCount:
                                    // Simply inverse them
                                    foreach (var serviceUnit in outputValues.Keys.ToList())
                                    {
                                        outputValues[serviceUnit] = 1.0 / outputValues[serviceUnit];
                                    }
                                    break;

                                default:
                                    // Simply flip them (next step will convert the numbers to positive ones again)
                                    foreach (var serviceUnit in outputValues.Keys.ToList())
                                    {
                                        outputValues[serviceUnit] *= -1;
                                    }
                                    break;
                                }
                            }
                            // If there is a negative value, shift all values to positive ones
                            double minOutputValue = outputValues.Min(v => v.Value);
                            if (minOutputValue < 0)
                            {
                                foreach (var serviceUnit in outputValues.Keys.ToList())
                                {
                                    outputValues[serviceUnit] += Math.Abs(minOutputValue);
                                }
                            }
                            // Add constraint
                            if (_config.InputOriented)
                            {
                                model.AddConstr(
                                    // Sum of all other weighted inputs
                                    LinearExpression.Sum(_serviceUnits.Select(s => outputValues[s]), _serviceUnits.Select(s => weights[s])) >=
                                    // Shall be smaller than the weighted input of the service unit in focus
                                    outputValues[serviceUnitInFocus], "Output" + outputEntry);
                            }
                            else
                            {
                                model.AddConstr(
                                    // Sum of all other weighted inputs
                                    LinearExpression.Sum(_serviceUnits.Select(s => outputValues[s]), _serviceUnits.Select(s => weights[s])) >=
                                    // Shall be smaller than the weighted input of the service unit in focus
                                    efficiencyRating * outputValues[serviceUnitInFocus], "Output" + outputEntry);
                            }
                        }
                    }
                    else
                    {
                        // Add constant uniform outputs for all service units
                        if (_config.InputOriented)
                        {
                            model.AddConstr(
                                // Sum of all other weighted inputs
                                LinearExpression.Sum(_serviceUnits.Select(s => 1.0), _serviceUnits.Select(s => weights[s])) >=
                                // Shall be smaller than the weighted input of the service unit in focus
                                1.0, "OutputConstant");
                        }
                        else
                        {
                            model.AddConstr(
                                // Sum of all other weighted inputs
                                LinearExpression.Sum(_serviceUnits.Select(s => 1.0), _serviceUnits.Select(s => weights[s])) >=
                                // Shall be smaller than the weighted input of the service unit in focus
                                efficiencyRating * 1.0, "OutputConstant");
                        }
                    }

                    // Sum weights
                    if (_config.WeightsSumToOne)
                    {
                        // Summed weights have to be equal to one
                        model.AddConstr(LinearExpression.Sum(_serviceUnits.Select(s => weights[s])) == 1.0, "WeightSum");
                    }

                    // Commit changes
                    model.Update();

                    // --> Solve model
                    model.Optimize();

                    // --> Get solution
                    if (!model.HasSolution())
                    {
                        throw new InvalidOperationException("Model is infeasible for service unit: " + serviceUnitInFocus.Ident);
                    }
                    _serviceUnitScores[groupIdents, serviceUnitInFocus] = efficiencyRating.Value;
                    _config.LogLine(" - Efficiency: " + (efficiencyRating.Value * 100).ToString(IOConstants.FORMATTER) + " %");
                }

                // Transform efficiency
                if (!_config.InputOriented && _config.TransformOutputOrientedEfficiency)
                {
                    //double maxEfficiency = _serviceUnits.Max(s => _serviceUnitScores[groupIdents, s]);
                    foreach (var serviceUnit in _serviceUnits)
                    {
                        _serviceUnitScores[groupIdents, serviceUnit] = 1.0 / _serviceUnitScores[groupIdents, serviceUnit];
                    }
                    // _serviceUnitScores[groupIdents, serviceUnit] /= maxEfficiency;
                }
            }
        }