/// <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)); }
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; } } }