private static Model NewHelper(out Variable[] variables, Func <int, bool> isIntegerFunc, Func <int, string> columnNameFunc, Func <int, string> rowNameFunc, double[] colLower, double[] colUpper, string objName, double[] objCoefs, int numberVariables, int numberConstraints, char[] rowSenses, CoinPackedMatrix rowMatrix, double[] rowLowers, double[] rowUppers) { Model model = new Model(); variables = new Variable[numberVariables]; Expression objExpr = new Expression(); int i = 0; for (i = 0; i < numberVariables; i++) { Variable var = new Variable(); variables[i] = var; double lower = colLower[i]; double upper = colUpper[i]; bool isInteger = isIntegerFunc.Invoke(i); string name = columnNameFunc.Invoke(i); if (!string.IsNullOrEmpty(name)) { var.Name = name; } else { var.Name = string.Concat("VAR", i); } var.Lower = lower; var.Upper = upper; var.Type = (isInteger) ? VariableType.Integer : VariableType.Continuous; objExpr.Add(objCoefs[i], var); } model.Objective = new Objective(objName, objExpr); model.ObjectiveSense = ObjectiveSense.Minimise; // NOTE: MPS DOESNT STORE MAXIMIZATION OR MINIMIZATION! // bUT LP always returns Minimization (and transforms objective accordingly if original is max) for (int j = 0; j < numberConstraints; j++) { Expression expr = new Expression(); CoinShallowPackedVector vector = rowMatrix.getVector(j); // I guess.. int nElements = vector.getNumElements(); int[] indices = vector.getIndices(); double[] elements = vector.getElements(); for (int e = 0; e < nElements; e++) { int index = indices[e]; Variable var = variables[index]; double coef = elements[e]; expr.Add(coef, var); } double lower = rowLowers[j]; double upper = rowUppers[j]; string name = rowNameFunc.Invoke(j); string conName; if (!string.IsNullOrEmpty(name)) { conName = name; } else { conName = string.Concat("CON", i); } switch (rowSenses[j]) { case 'L': //<= constraint and rhs()[i] == rowupper()[i] { ConstraintType type = ConstraintType.LE; Expression upperExpr = new Expression(upper); Constraint con = new Constraint(conName, expr, type, upperExpr); upperExpr.Clear(); model.Add(con); break; } case 'E': //= constraint { ConstraintType type = ConstraintType.EQ; Expression upperExpr = new Expression(upper); Constraint con = new Constraint(conName, expr, type, upperExpr); upperExpr.Clear(); model.Add(con); break; } case 'G': //>= constraint and rhs()[i] == rowlower()[i] { ConstraintType type = ConstraintType.GE; Expression lowerExpr = new Expression(lower); Constraint con = new Constraint(conName, expr, type, lowerExpr); lowerExpr.Clear(); model.Add(con); break; } case 'R': //ranged constraint { RangeConstraint con = new RangeConstraint(conName, lower, expr, upper); model.Add(con); break; } case 'N': //free constraint { RangeConstraint con = new RangeConstraint(conName, lower, expr, upper); con.Enabled = false; model.Add(con); break; } default: break; } } return(model); }
/// <summary> /// Creates a new model from the given file. The name of the model will be the filename. /// </summary> /// <param name="fileName">The mps or lp file to be imported.</param> /// <param name="variables">The full array of variables created for this new model.</param> /// <returns>The new model, or an exception if an error occurred.</returns> public static Model New(string fileName, out Variable[] variables) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); string extension = Path.GetExtension(fileName).ToLower(); Model model = null; if (extension.Equals(".mps")) { #region New Model from .mps file string directoryName = Path.GetDirectoryName(fileName); if (directoryName.Length == 0) { directoryName = "."; } string fullPathWithoutExtension = Path.Combine(directoryName, fileNameWithoutExtension); // Previously, CoinMpsIO was used but that doesnt read quad info. // To read MPS with QUAD info use ClpModel or CoinModel readMps. // CoinModel we havent Wrapped yet at all, so more work. // ClpModel (ClpSimplex) doesnt do row Sense needed for here, only lb / ub. // ClpModel also doesnt save the Objective name. // OsiClp can create row sense from lb / ub, but OsiClp uses CoinMpsIO, so cannot read Quad from OsiClp // So we use ClpSimplex to read the file, but use an OsiClp of it for rowsense ClpSimplex m = new ClpSimplex(); OsiClpSolverInterface osiClp = new OsiClpSolverInterface(m); log.PassToClpModel(m); int numberErrors = m.readMps(fileName, true, false); if (numberErrors != 0) { string message = string.Format("Errors occurred when reading the mps file '{0}'.", fileName); SonnetLog.Default.Error(message); throw new SonnetException(message); } // set objective function offest // Skip this: setDblParam(OsiObjOffset,m.objectiveOffset()) //ClpModel has ClpObjective which may be an implementatin of ClpQuadObjective. // This can be found by obj->type() == 2 (QuadCoef) bool fullQuadraticMatrix = false; CoinPackedMatrix quadraticObjective = null; ClpObjective clpObjective = m.objectiveAsObject(); if (clpObjective is ClpQuadraticObjective clpQuadraticObjective) { Ensure.IsTrue(clpObjective.type() == 2, $"Quadratic Objective must be type 2 but is {clpObjective.type()}."); fullQuadraticMatrix = clpQuadraticObjective.fullMatrix(); quadraticObjective = clpQuadraticObjective.quadraticObjective(); } model = NewHelper(out variables, m.isInteger, m.columnName, m.rowName, m.getColLower(), m.getColUpper(), "OBJROW", m.getObjCoefficients(), m.getNumCols(), m.getNumRows(), osiClp.getRowSense(), osiClp.getMatrixByRow(), m.getRowLower(), m.getRowUpper(), fullQuadraticMatrix, quadraticObjective); model.Name = fileNameWithoutExtension; #endregion } else if (extension.Equals(".lp")) { #region New Model from .lp file CoinLpIO m = new CoinLpIO(); log.PassToCoinLpIO(m); m.setInfinity(MathUtils.Infinity); // If the original problem is // a maximization problem, the objective function is immediadtly // flipped to get a minimization problem. m.readLp(fileName); model = NewHelper(out variables, m.isInteger, m.columnName, m.rowName, m.getColLower(), m.getColUpper(), m.getObjName(), m.getObjCoefficients(), m.getNumCols(), m.getNumRows(), m.getRowSense(), m.getMatrixByRow(), m.getRowLower(), m.getRowUpper(), false, null); model.Name = fileNameWithoutExtension; #endregion } else { string message = string.Format("Cannot import file {0} : unknown extension '{1}'.", fileName, extension); SonnetLog.Default.Error(message); throw new SonnetException(message); } if (model == null) { string message = $"An error occured while importing {fileName}. No model created."; SonnetLog.Default.Error(message); throw new SonnetException(message); } return(model); }