Skip to content

Commit

Permalink
Add Osi version, objective offset
Browse files Browse the repository at this point in the history
Revert to supporting QP but not MIQP
  • Loading branch information
jhmgoossens committed Dec 23, 2024
1 parent c9a4161 commit fdae11d
Show file tree
Hide file tree
Showing 16 changed files with 229 additions and 107 deletions.
10 changes: 5 additions & 5 deletions .coin-or/Dependencies
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
../pthreads https://github.com/GerHobbelt/pthread-win32 master
Data/Sample https://github.com/coin-or-tools/Data-Sample stable/1.2
Data/miplib3 https://github.com/coin-or-tools/Data-miplib3 stable/1.2
CoinUtils https://github.com/coin-or/CoinUtils stable/2.11
Osi https://github.com/coin-or/Osi stable/0.108
Clp https://github.com/coin-or/Clp stable/1.17
Cgl https://github.com/coin-or/Cgl stable/0.60
Cbc https://github.com/coin-or/Cbc releases/2.10.11
CoinUtils https://github.com/coin-or/CoinUtils releases/2.11.12
Osi https://github.com/coin-or/Osi releases/0.108.11
Clp https://github.com/coin-or/Clp releases/1.17.10
Cgl https://github.com/coin-or/Cgl releases/0.60.9
Cbc https://github.com/coin-or/Cbc releases/2.10.12
35 changes: 34 additions & 1 deletion MSVisualStudio/v17/Sonnet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sonnet - NET4", "Sonnet - N
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libOsiCommonTest", "..\..\..\Osi\Osi\MSVisualStudio\v17\libOsiCommonTest\libOsiCommonTest.vcxproj", "{109D6E6F-6D91-460F-86AE-DF27400E09A9}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cbc", "..\..\..\Cbc\Cbc\MSVisualStudio\v17\cbc\cbc.vcxproj", "{EAF01AC1-8D19-4E37-9852-736DCFF67865}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cbc", "..\..\..\cbc\Cbc\MSVisualStudio\v17\cbc\cbc.vcxproj", "{EAF01AC1-8D19-4E37-9852-736DCFF67865}"
ProjectSection(ProjectDependencies) = postProject
{79433425-EC16-410F-9B91-8D31D2109C90} = {79433425-EC16-410F-9B91-8D31D2109C90}
{6D2EF92A-D693-47E3-A325-A686E78C5FFD} = {6D2EF92A-D693-47E3-A325-A686E78C5FFD}
Expand Down Expand Up @@ -86,6 +86,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".coin-or", ".coin-or", "{DF
..\..\.coin-or\projDesc.xml = ..\..\.coin-or\projDesc.xml
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Clp", "..\..\..\clp\Clp\MSVisualStudio\v17\Clp\Clp.vcxproj", "{E62F88EE-DC76-489A-99A8-8070099D9D4F}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "osiUnitTest", "..\..\..\clp\Clp\MSVisualStudio\v17\osiUnitTest\osiUnitTest.vcxproj", "{75367352-81A3-4707-8560-3005EDC37BBA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Clp", "Clp", "{00148231-272C-403B-A3B8-1379293531AA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Expand Down Expand Up @@ -286,6 +292,30 @@ Global
{EAF01AC1-8D19-4E37-9852-736DCFF67865}.Release|x86.Build.0 = Release|Win32
{EAF01AC1-8D19-4E37-9852-736DCFF67865}.ReleaseParallel|x64.ActiveCfg = ReleaseParallel|x64
{EAF01AC1-8D19-4E37-9852-736DCFF67865}.ReleaseParallel|x86.ActiveCfg = ReleaseParallel|Win32
{E62F88EE-DC76-489A-99A8-8070099D9D4F}.Debug|x64.ActiveCfg = Debug|x64
{E62F88EE-DC76-489A-99A8-8070099D9D4F}.Debug|x64.Build.0 = Debug|x64
{E62F88EE-DC76-489A-99A8-8070099D9D4F}.Debug|x86.ActiveCfg = Debug|Win32
{E62F88EE-DC76-489A-99A8-8070099D9D4F}.Debug|x86.Build.0 = Debug|Win32
{E62F88EE-DC76-489A-99A8-8070099D9D4F}.Release|x64.ActiveCfg = Release|x64
{E62F88EE-DC76-489A-99A8-8070099D9D4F}.Release|x64.Build.0 = Release|x64
{E62F88EE-DC76-489A-99A8-8070099D9D4F}.Release|x86.ActiveCfg = Release|Win32
{E62F88EE-DC76-489A-99A8-8070099D9D4F}.Release|x86.Build.0 = Release|Win32
{E62F88EE-DC76-489A-99A8-8070099D9D4F}.ReleaseParallel|x64.ActiveCfg = Release|x64
{E62F88EE-DC76-489A-99A8-8070099D9D4F}.ReleaseParallel|x64.Build.0 = Release|x64
{E62F88EE-DC76-489A-99A8-8070099D9D4F}.ReleaseParallel|x86.ActiveCfg = Release|Win32
{E62F88EE-DC76-489A-99A8-8070099D9D4F}.ReleaseParallel|x86.Build.0 = Release|Win32
{75367352-81A3-4707-8560-3005EDC37BBA}.Debug|x64.ActiveCfg = Debug|x64
{75367352-81A3-4707-8560-3005EDC37BBA}.Debug|x64.Build.0 = Debug|x64
{75367352-81A3-4707-8560-3005EDC37BBA}.Debug|x86.ActiveCfg = Debug|Win32
{75367352-81A3-4707-8560-3005EDC37BBA}.Debug|x86.Build.0 = Debug|Win32
{75367352-81A3-4707-8560-3005EDC37BBA}.Release|x64.ActiveCfg = Release|x64
{75367352-81A3-4707-8560-3005EDC37BBA}.Release|x64.Build.0 = Release|x64
{75367352-81A3-4707-8560-3005EDC37BBA}.Release|x86.ActiveCfg = Release|Win32
{75367352-81A3-4707-8560-3005EDC37BBA}.Release|x86.Build.0 = Release|Win32
{75367352-81A3-4707-8560-3005EDC37BBA}.ReleaseParallel|x64.ActiveCfg = Release|x64
{75367352-81A3-4707-8560-3005EDC37BBA}.ReleaseParallel|x64.Build.0 = Release|x64
{75367352-81A3-4707-8560-3005EDC37BBA}.ReleaseParallel|x86.ActiveCfg = Release|Win32
{75367352-81A3-4707-8560-3005EDC37BBA}.ReleaseParallel|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -313,6 +343,9 @@ Global
{A014E79E-781F-4CB6-AD0B-27A75AF62755} = {53EB451D-0B18-4046-9038-1CDD196DDF33}
{A9D23D25-E8F4-4BE6-8699-C9A06FB0B582} = {53EB451D-0B18-4046-9038-1CDD196DDF33}
{DF264510-2EE5-4681-8FEB-3C13274A8C62} = {F48F0304-01B7-4EF7-93B3-85F297B15C44}
{E62F88EE-DC76-489A-99A8-8070099D9D4F} = {00148231-272C-403B-A3B8-1379293531AA}
{75367352-81A3-4707-8560-3005EDC37BBA} = {00148231-272C-403B-A3B8-1379293531AA}
{00148231-272C-403B-A3B8-1379293531AA} = {D7A6A624-56FF-4177-9382-DEBFDA33E5B5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1C418B8B-47F5-4DF0-B50B-4F5FBAEE968D}
Expand Down
29 changes: 10 additions & 19 deletions src/Sonnet/Model.cs
Original file line number Diff line number Diff line change
Expand Up @@ -317,28 +317,17 @@ public static Model New(string fileName, string fileType, out Variable[] variabl

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);
OsiClpSolverInterface osiClp = new OsiClpSolverInterface();
log.PassToSolver(osiClp);

int numberErrors = osiClp.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())
ClpSimplex m = osiClp.getModelPtr();

//ClpModel has ClpObjective which may be an implementatin of ClpQuadObjective.
// This can be found by obj->type() == 2 (QuadCoef)
Expand All @@ -354,7 +343,7 @@ public static Model New(string fileName, string fileType, out Variable[] variabl
}

model = NewHelper(out variables, m.isInteger, m.columnName, m.rowName,
m.getColLower(), m.getColUpper(), "OBJROW", m.getObjCoefficients(),
m.getColLower(), m.getColUpper(), "OBJROW", m.getObjCoefficients(), m.objectiveOffset(),
m.getNumCols(), m.getNumRows(), osiClp.getRowSense(), osiClp.getMatrixByRow(), m.getRowLower(), m.getRowUpper(), fullQuadraticMatrix, quadraticObjective);

// Ensure osiClp is not disposed right after a shallow copy of getMatrixByRow was taken for NewHelper call.
Expand All @@ -377,7 +366,7 @@ public static Model New(string fileName, string fileType, out Variable[] variabl
m.readLp(fileName);

model = NewHelper(out variables, m.isInteger, m.columnName, m.rowName,
m.getColLower(), m.getColUpper(), m.getObjName(), m.getObjCoefficients(),
m.getColLower(), m.getColUpper(), m.getObjName(), m.getObjCoefficients(), m.objectiveOffset(),
m.getNumCols(), m.getNumRows(), m.getRowSense(), m.getMatrixByRow(), m.getRowLower(), m.getRowUpper(), false, null);

GC.KeepAlive(m);
Expand Down Expand Up @@ -413,6 +402,7 @@ public static Model New(string fileName, string fileType, out Variable[] variabl
/// <param name="colUpper"></param>
/// <param name="objName"></param>
/// <param name="objCoefs"></param>
/// <param name="objConst"></param>
/// <param name="numberVariables"></param>
/// <param name="numberConstraints"></param>
/// <param name="rowSenses"></param>
Expand All @@ -424,13 +414,14 @@ public static Model New(string fileName, string fileType, out Variable[] variabl
/// <returns></returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Private member and by design")]
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, bool fullQuadraticMatrix, CoinPackedMatrix quadraticObjective)
double[] colLower, double[] colUpper, string objName, double[] objCoefs, double objConst, int numberVariables, int numberConstraints, char[] rowSenses, CoinPackedMatrix rowMatrix, double[] rowLowers, double[] rowUppers, bool fullQuadraticMatrix, CoinPackedMatrix quadraticObjective)
{
Model model = new Model();
variables = new Variable[numberVariables];

Expression objExpr = new Expression();

objExpr.Subtract(objConst); // yes, subtract
for (int i = 0; i < numberVariables; i++)
{
Variable var = new Variable();
Expand Down
63 changes: 13 additions & 50 deletions src/Sonnet/Solver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public class Solver : Named, IDisposable
/// Initializes a new instance of the Solver class with the given name and model,
/// and using the given instance derived from OsiSolverInterface.
/// QP is supported for OsiClp and OsiCbc only.
/// MIQP is supported for OsiCbcsolver only.
/// MIQP is not supported for any solver type.
/// </summary>
/// <param name="model">The model used in this solver.</param>
/// <param name="solver">The instance of an OsiSolver, eg, OsiClpSolverInterface to be used.</param>
Expand All @@ -49,7 +49,7 @@ public Solver(Model model, OsiSolverInterface solver, string name = null)
/// Initializes a new instance of the Solver class with the given name and model,
/// and using a to be constructed instance of the given type derived from OsiSolverInterface.
/// QP is supported for OsiClp and OsiCbc only.
/// MIQP is supported for OsiCbcsolver only.
/// MIQP is not supported for any solver type.
/// </summary>
/// <param name="model">The model used in this solver.</param>
/// <param name="osiSolverInterfaceType">The type derived from OsiSolverInterface to be used.</param>
Expand Down Expand Up @@ -245,7 +245,11 @@ public Model Model
/// </summary>
public OsiSolverInterface OsiSolver
{
get { return this.solver; }
get
{
if (!IsGenerated && model != null && model.Constraints.Any()) log.Warn("Calling OsiSolver before generating will use an empty model. Consider calling Generate() earlier.");
return this.solver;
}
}

/// <summary>
Expand Down Expand Up @@ -282,7 +286,6 @@ internal void ApplyObjective(Objective objective)

this.objective.Unregister(this);
this.objective = objective;
// if the new or old objective are Quadratic, then ungenerate first
Generate(this.objective);
}
else
Expand Down Expand Up @@ -381,10 +384,7 @@ private void Solve(bool doResolve, bool forceRelaxation)
{
if (forceRelaxation == false && IsMIP)
{
if (objective.IsQuadratic)
{
log.Warn("Only experimantal support for MIQP!");
}
if (objective.IsQuadratic) throw new NotSupportedException("MIQP not supported");

isSolving = true;
SaveBeforeMIPSolveInternal(); // save anyway, to allow manual reset
Expand Down Expand Up @@ -735,8 +735,7 @@ private void Generate(Objective obj)

if (IsGenerated)
{
// WARNING: This exception could be due to an implicit call to Generate by a Debugger Local or Watch evaluation at a breakpoint.
if (obj.IsQuadratic) throw new NotSupportedException("Updating the quadratic objective is not supported for already generated models.");
if (obj.IsQuadratic) throw new NotSupportedException("Generating the quadratic objective is not supported for already generated models.");

// now load the objective into the solver
int n = variables.Count; // the NEW number of variables
Expand Down Expand Up @@ -906,44 +905,6 @@ public void Generate()
}
rawconstraints.Clear();

#if (SONNET_USE_SEMICONTVAR)
// now that all the regular constraints and variables are registered,
// register the additional constraints and helper variables for any semi-continuous variables
foreach (Variable var in variables.Where(v => v is SemiContiniuousVariable))
{
SemiContinuousVariable scvar = (SemiContinuousVariable)var;
double sclower = scvar.SemiContinuousLower;
double upper = scvar.Upper;
string name = scvar.Name + "SCHelper";

Variable scvarHelper = new Variable(name, 0.0, 1.0, VariableType.Integer);

Constraint con1 = new Constraint(name + "Con1", 1.0 * scvar, ConstraintType.LE, upper * scvarHelper);
try
{
Generate(con1);
}
catch (Exception ex)
{
string message = string.Format("Error generating constraint {0}.", con1.Name);
throw new SonnetException(message, ex);
}
nz += con1.Coefficients.Count;

Constraint con2 = new Constraint(name + "Con2", sclower * scvarHelper, ConstraintType.LE, 1.0 * scvar);
try
{
Generate(con2);
}
catch (Exception ex)
{
string message = string.Format("Error generating constraint {0}.", con2.Name);
throw new SonnetException(message, ex);
}
nz += con2.Coefficients.Count;
}
#endif

log.DebugFormat("Done generating matrix after ", (CoinUtils.CoinCpuTime() - genStart));

unsafe
Expand Down Expand Up @@ -1195,17 +1156,18 @@ given in a standard column major ordered format (without gaps). */
{
log.Debug("Using CLP-specific quadratic objective loading.");

if (isMip) log.Warn("Generating MIQP, but solving MIQP with OsiClp is not supported.");

ClpSimplex clpSimplex = osiClp.getModelPtr();

clpSimplex.loadQuadraticObjectiveUnsafe(n, startObj, columnObj, elementObj);
//clpSimplex.writeMps("testquad.mps", 0, 1);// for CPLEX compatibility, use formatType = 0, numberAcross = 1);
}
else if (solver is OsiCbcSolverInterface osiCbc)
{
// TODO: does QP with Cbc work? Does MIQP with Cbc work?
log.Debug("Using CBC-specific quadratic objective loading.");

if (isMip) log.Warn("Only experimantal support for MIQP!");
if (isMip) log.Warn("Generating MIQP, but solving MIQP with OsiCbc is not supported.");

OsiSolverInterface osiReal = osiCbc.getRealSolverPtr(); //usually the OsiClpSolver
if (osiReal is OsiClpSolverInterface osiRealClp)
Expand Down Expand Up @@ -1296,6 +1258,7 @@ given in a standard column major ordered format (without gaps). */
/// Ungenerate the model.
/// If you're looking to reset the solver, simply create a new instance of solver.
/// </summary>

public void UnGenerate()
{
if (IsGenerated)
Expand Down
8 changes: 8 additions & 0 deletions src/SonnetWrapper/CbcSolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ namespace COIN
}
}

static property System::String^ Version
{
System::String^ get()
{
return gcnew String(CBC_VERSION);
}
}

/// <summary>
/// The CallBack to delegate to be invoked.
/// If you add multiple delegates, all will be invoked,
Expand Down
5 changes: 5 additions & 0 deletions src/SonnetWrapper/ClpModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ namespace COIN
return result;
}

const double objectiveOffset()
{
return Base->objectiveOffset();
}

bool isInteger(int columnNumber)
{
return Base->isInteger(columnNumber);
Expand Down
5 changes: 5 additions & 0 deletions src/SonnetWrapper/CoinLpIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ namespace COIN
return result;
}

const double objectiveOffset()
{
return Base->objectiveOffset();
}

/// Return the problem name
String^ getProblemName()
{
Expand Down
8 changes: 8 additions & 0 deletions src/SonnetWrapper/OsiCbcSolverInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ namespace COIN
return Derived->getNodeCount();
}

virtual property System::String^ Version
{
System::String^ get() override
{
return gcnew String(CBC_VERSION);
}
}

protected:
property ::OsiCbcSolverInterface * Derived
{
Expand Down
30 changes: 30 additions & 0 deletions src/SonnetWrapper/OsiClpSolverInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,41 @@ namespace COIN
return gcnew CoinPackedMatrix(Base->getMatrixByRow());
}

/// <summary>
/// Read an mps file from the given filename.
/// </summary>
/// <param name="fileName">File to be read</param>
/// <param name="keepNames">True to use the given row and column names</param>
/// <param name="ignoreErrors">True to ignore errors during reading of the file</param>
/// <returns>Status non-zero represents an error</returns>
int readMps(String^ fileName, bool keepNames, bool ignoreErrors)
{
try
{
char* charFileName = (char*)Marshal::StringToHGlobalAnsi(fileName).ToPointer();
int result = ((::OsiClpSolverInterface *) Base)->readMps(charFileName, keepNames, ignoreErrors);
Marshal::FreeHGlobal((IntPtr)charFileName);
return result;
}
catch (::CoinError err)
{
throw gcnew CoinError(err);
}
}

/// Get pointer to column-wise copy of matrix
CoinPackedMatrix^ getMatrixByCol()
{
return gcnew CoinPackedMatrix(Base->getMatrixByCol());
}

virtual property System::String^ Version
{
System::String^ get() override
{
return gcnew String(CLP_VERSION);
}
}
};

/* // Uncomment for CPLEX support
Expand Down
Loading

0 comments on commit fdae11d

Please sign in to comment.