ERGO-Code / HiGHS

Linear optimization software
MIT License
920 stars 173 forks source link

Enable QP handling in C# and tidy up the use of HighsModel #1900

Closed jajhall closed 1 week ago

jajhall commented 2 weeks ago

The C# interface cannot handle QPs, so some mechanism needs to be added.

I can add a C# call to Highs::passHessian that allows a Hessian term to be added to the incumbent LP

In C++ the Hessian is held as an instance of the HighsHessian class, and a QP is represented as an instance of the HighsModel class - that consists of a HighsLp and a HighsHessian instance.

In C# there is a "HighsModel" object that corresponds to (a subset of) the data members of the HighsLp class, which is a bit of a mess. This should be sorted so that the C# there is a "HighsModel" object corresponds to the C++ HighsModel class, and something else (in C#) relates to HighsLp class.

But this would break the C# API...

@Thiago-NovaesB: are you in a position to advise/help us on this

Thiago-NovaesB commented 2 weeks ago

I believe that exposing the internal structures of HiGHS via API is not a good idea. I really like the API created by SCIP, I recently made an interface in C# for it and we can use some ideas from there. SCIP exposes pointers to its structures via API. The API methods receive these pointers. So these structures do not need to be replicated in each API. Some examples:

The Create method just give us the pointer to the main scip struct: private static partial ScipRetCode SCIPcreate(ref IntPtr scip); // SCIP_RETCODE SCIPcreate(SCIP** scip)

The get solution receive just the main struct pointer and returns the pointer to the solution struct: private static partial IntPtr SCIPgetBestSol(IntPtr scip); // SCIP_SOL* SCIPgetBestSol(SCIP* scip)

To get the value of a variable in the solution, just pass the main struct pointer, solution pointer and variable pointer: private static partial double SCIPgetSolVal(IntPtr scip, IntPtr sol, IntPtr v); // SCIP_Real SCIPgetSolVal(SCIP* scip, SCIP_SOL* sol, SCIP_VAR* var)

Thiago-NovaesB commented 2 weeks ago

Do you think that we can do the same in HiGHS? Share the pointers to avoid replicate structs?

jajhall commented 2 weeks ago

If I understand correctly,

However, all this call does is pass the individual components of model (plus the scalar dimensions deduced from lengths and casting enums to int) into the C API method.

Other than saving a user from having identifiers for the number of rows, columns and nonzeros, this doesn't achieve a lot. But I'm not a C# user, so I don't know whether it's appreciated by anyone.

Note that I've just edited call_highs_from_csharp.cs heavily, since the LP it formed was infeasible! I've changed the LP completely and, after solving it, I've added a Hessian and solved again.

Thiago-NovaesB commented 2 weeks ago

"In principle this would make the HiGHS C# API similar to its C API, where the parameter lists consist only of scalars and pointers to vectors." I dont think so. I'm talking about create strucs in C# just to keep the C++ pointer private (so the user of C# API dont care about how C++ works):

public struct ScipVariable
{
    private IntPtr ptr;
}
public struct ScipConstraint
{
    private IntPtr ptr;
}
public struct ScipSolution
{
    private IntPtr ptr;
}

Then the C# API uses will looks like:

ScipVariable x = solver.CreateVariable("xVar", 0d, 1d, -1d, ScipVartype.SCIP_VARTYPE_CONTINUOUS);
ScipVariable y = solver.CreateVariable("yVar", 0.5, 1d, 1d, ScipVartype.SCIP_VARTYPE_BINARY);
ScipVariable z = solver.CreateVariable("zVar", 0.5, 1d, 1d, ScipVartype.SCIP_VARTYPE_BINARY);
solver.DeleteVariable(y);
solver.Solve();
ScipSolution sol = solver.GetBestSolution();
double xVal = solver.GetSolutionVariableValue(sol, x);

The C# API will use just private pointers, the one who will consumes the API will have a clean C# code.

The downside of this approach is that we need to expose many methods for all types of necessary edits.

If you think this is not a good idea, we can continue with the current idea, replicating the structures in C#.

Thiago-NovaesB commented 2 weeks ago

I'm a little confused by your initial comment about breaking the C# API. Isn't it just necessary to add some fields in the HighsModel class to accommodate the Hessian?

jajhall commented 2 weeks ago

I still don't see what you mean, or appreciate the value of having "a clean C# code", as I'm not a C# user. Specifically, I don't see the difference between what you suggest and having

  HighsLpSolver solver = new HighsLpSolver();

where HighsLpSolver has a private IntPtr highs - the pointer to the HiGHS C++ instance?

Both then use solver.run() etc

How would "solver" be instantiated the way you would want.

I'm a little confused by your initial comment about breaking the C# API. Isn't it just necessary to add some fields in the HighsModel class to accommodate the Hessian?

Again, I'm displaying my almost complete ignorance of C#. I thought that if non-optional parameters were added to

public HighsModel(double[] colcost, ...

then code written for the original parameter list would fail due to having insufficient parameters.

jajhall commented 2 weeks ago

I'll leave the issue open and retain the branch, but I believe that what I've done works, and @LatonCypher wants to use it.

LatonCypher commented 2 weeks ago

Yeah. It works. I am Laton cypher. I am about to test it. I just modified the name of the solver. I now called it HighsSolver against HighsLpSolver.

Thanks so much.

Lateef


From: Julian Hall @.> Sent: Wednesday, August 28, 2024 9:36 PM To: ERGO-Code/HiGHS @.> Cc: Lateef kareem @.>; Mention @.> Subject: Re: [ERGO-Code/HiGHS] Enable QP handling in C# and tidy up the use of HighsModel (Issue #1900)

I'll leave the issue open and retain the branch, but I believe that what I've done works, and @LatonCypherhttps://github.com/LatonCypher wants to use it.

— Reply to this email directly, view it on GitHubhttps://github.com/ERGO-Code/HiGHS/issues/1900#issuecomment-2316207197, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AW46QB4ZJJG464ZYG7TPTOTZTYYGLAVCNFSM6AAAAABNIOFTHCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGMJWGIYDOMJZG4. You are receiving this because you were mentioned.Message ID: @.***>

Thiago-NovaesB commented 2 weeks ago

Sorry, I hadn't seen your branch and I was confused about what exactly you wanted. In fact, if the parameter is not optional, it needs to be passed. But couldn't you do the same as you did with MIP? Keep null as default:

public class HighsModel
{
    public HighsObjectiveSense sense;
    public double[] colcost;
    public double offset;
    public double[] collower;
    public double[] colupper;
    public double[] rowlower;
    public double[] rowupper;
    public HighsMatrixFormat a_format;
    public int[] astart;
    public int[] aindex;
    public double[] avalue;
    public int[] highs_integrality;
    public HessianFormat? q_format;
    public int[] qstart;
    public int[] qindex;
    public double[] qvalue;

    public HighsModel()
    {

    }

    public HighsModel(double[] colcost, double[] collower, double[] colupper, double[] rowlower, double[] rowupper,
    int[] astart, int[] aindex, double[] avalue, int[] highs_integrality = null, double offset = 0, HighsMatrixFormat a_format = HighsMatrixFormat.kColwise, HighsObjectiveSense sense = HighsObjectiveSense.kMinimize, HessianFormat? q_format = null, int[]? qstart = null,int[]? qindex = null, double[]? qvalue = null)
    {
        this.colcost = colcost;
        this.collower = collower;
        this.colupper = colupper;
        this.rowlower = rowlower;
        this.rowupper = rowupper;
        this.astart = astart;
        this.aindex = aindex;
        this.avalue = avalue;
        this.offset = offset;
        this.a_format = a_format;
        this.sense = sense;
        this.highs_integrality = highs_integrality;
        this.qstart = qstart;
        this.qindex = qindex;
        this.qvalue = qvalue;
        this.q_format = q_format;
    }
}

And then:

public HighsStatus passModel(HighsModel model)
{
    return (HighsStatus)HighsLpSolver.Highs_passModel(
        this.highs,
        model.colcost.Length,
        model.rowlower.Length,
        model.avalue.Length,
        model.qvalue.Length,
        (int)model.a_format,
        (int)model.q_format,
        (int)model.sense,
        model.offset,
        model.colcost,
        model.collower,
        model.colupper,
        model.rowlower,
        model.rowupper,
        model.astart,
        model.aindex,
        model.avalue,
        model.qstart,
        model.qindex,
        model.qvalue,
        model.highs_integrality);
}
Thiago-NovaesB commented 2 weeks ago

Anyway, I'll think a bit about how to improve this interface, if I have an interesting idea, I'll open a PR for you to check what you think.

LatonCypher commented 2 weeks ago

I think this modification by Thiago is another improvement. So I can use the same constructor to make but LPModel and QPmodel. If I am making LP, I just stop after highs_integrality since parameters that follow are optional. And if I have a QP problem, then I list the hessian parameters in the call to the model constructor and every other things follow just as for the Lp.

Thank you for all your work.

Lateef


From: Thiago Novaes @.> Sent: Wednesday, August 28, 2024 11:03 PM To: ERGO-Code/HiGHS @.> Cc: Lateef kareem @.>; Mention @.> Subject: Re: [ERGO-Code/HiGHS] Enable QP handling in C# and tidy up the use of HighsModel (Issue #1900)

Sorry, I hadn't seen your branch and I was confused about what exactly you wanted. In fact, if the parameter is not optional, it needs to be passed. But couldn't you do the same as you did with MIP? Keep null as default:

public class HighsModel { public HighsObjectiveSense sense; public double[] colcost; public double offset; public double[] collower; public double[] colupper; public double[] rowlower; public double[] rowupper; public HighsMatrixFormat a_format; public int[] astart; public int[] aindex; public double[] avalue; public int[] highs_integrality; public HessianFormat? q_format; public int[] qstart; public int[] qindex; public double[] qvalue;

public HighsModel()
{

}

public HighsModel(double[] colcost, double[] collower, double[] colupper, double[] rowlower, double[] rowupper,
int[] astart, int[] aindex, double[] avalue, int[] highs_integrality = null, double offset = 0, HighsMatrixFormat a_format = HighsMatrixFormat.kColwise, HighsObjectiveSense sense = HighsObjectiveSense.kMinimize, HessianFormat? q_format = null, int[]? qstart = null,int[]? qindex = null, double[]? qvalue = null)
{
    this.colcost = colcost;
    this.collower = collower;
    this.colupper = colupper;
    this.rowlower = rowlower;
    this.rowupper = rowupper;
    this.astart = astart;
    this.aindex = aindex;
    this.avalue = avalue;
    this.offset = offset;
    this.a_format = a_format;
    this.sense = sense;
    this.highs_integrality = highs_integrality;
    this.qstart = qstart;
    this.qindex = qindex;
    this.qvalue = qvalue;
    this.q_format = q_format;
}

}

And then:

public HighsStatus passModel(HighsModel model) { return (HighsStatus)HighsLpSolver.Highs_passModel( this.highs, model.colcost.Length, model.rowlower.Length, model.avalue.Length, model.qvalue.Length, (int)model.a_format, (int)model.q_format, (int)model.sense, model.offset, model.colcost, model.collower, model.colupper, model.rowlower, model.rowupper, model.astart, model.aindex, model.avalue, model.qstart, model.qindex, model.qvalue, model.highs_integrality); }

— Reply to this email directly, view it on GitHubhttps://github.com/ERGO-Code/HiGHS/issues/1900#issuecomment-2316323217, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AW46QB7R2V22OKRTUA4YKRLZTZCJXAVCNFSM6AAAAABNIOFTHCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGMJWGMZDGMRRG4. You are receiving this because you were mentioned.

LatonCypher commented 2 weeks ago

@jajhall and @Thiago-NovaesB thank you so much for the contribution. I have used both original modification by @jajhall and then the additional modification by @Thiago-NovaesB . Both works well.

I have decided to go with the final modification by @Thiago-NovaesB. I have tested it and the results were consistent.

I also modified the info to contain the qp_Iteration_Count.

May be I should provide my version of "highs_csharp_api.cs".

jajhall commented 2 weeks ago

Now that I see that the QP-related (like the integrality parameter) can be made optional by passing null as default, I'm happy with the extension of Highs_passModel.

I also modified the info to contain the qp_Iteration_Count.

May be I should provide my version of "highs_csharp_api.cs".

Please do. Since it's just a single file, you can email it. This means that if I want to modify it, I don't have to create a new branch

LatonCypher commented 2 weeks ago

Thank you for all your contribution since yesterday. I have not completed and integration and tested all examples provided by Matlab here Quadratic programming - MATLAB quadprog (mathworks.com)https://www.mathworks.com/help/optim/ug/quadprog.html And gotten the same result.

I feel that LP in highs is just a special case of QP without Hessian (I am not sure I am correct since QP in Highs can not handle integer variable).

Attached here is the copy of my modifications to "highs_csharp_api.cs" file.

I have modified it to handle cases where Q might not be given, or the As of the inequality/equality might not be given.

And added the "qp_Iteration_Count" to the info.

Also, I set the default Matrixformat to "kRowwise"

I have also gotten rid of the passLp and passMip, I am now only having passModel.

So my QP and LP use of the highs now look very similar.

Because in my application, as I am indexing the number of wells, and the facility the flow passes through, the equalities and inequalites came naturally in compressed row form.

Note: We are planning to make our Mathematics Library open and freely available to everyone by the end of October. Is there anything we need to do as regards with our use of Highs?

Lateef A. Kareem

CypherCrescent Ltd.

76 Fiddil Ave.

Trans-amadi, PH

Tel: +2349091633519 Email: @.***


From: Julian Hall @.> Sent: Thursday, August 29, 2024 10:22 AM To: ERGO-Code/HiGHS @.> Cc: Lateef kareem @.>; Mention @.> Subject: Re: [ERGO-Code/HiGHS] Enable QP handling in C# and tidy up the use of HighsModel (Issue #1900)

Now that I see that the QP-related (like the integrality parameter) can be made optional by passing null as default, I'm happy with the extension of Highs_passModel.

I also modified the info to contain the qp_Iteration_Count.

May be I should provide my version of "highs_csharp_api.cs".

Please do. Since it's just a single file, you can email it. This means that if I want to modify it, I don't have to create a new branch

— Reply to this email directly, view it on GitHubhttps://github.com/ERGO-Code/HiGHS/issues/1900#issuecomment-2317128485, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AW46QB47XDSYQ4WHSC3HAQ3ZT3R6DAVCNFSM6AAAAABNIOFTHCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGMJXGEZDQNBYGU. You are receiving this because you were mentioned.Message ID: @.***>

using System; using System.Linq; using System.Runtime.InteropServices; using System.Text;

// mcs -out:highscslib.dll -t:library highs_csharp_api.cs -unsafe

namespace Highs { public enum HighsStatus { kError = -1, kOk, kWarning }

public enum HighsMatrixFormat
{
    kColwise = 1,
    kRowwise
}

public enum HessianFormat
{
    kTriangular = 1,
    kSquare
}

public enum HighsBasisStatus
{
    kLower = 0,
    kBasic,
    kUpper,
    kZero,
    kNonbasic
}

public enum HighsObjectiveSense
{
    kMinimize = 1,
    kMaximize = -1
}

public enum HighsModelStatus
{
    kNotset = 0,
    kLoadError,
    kModelError,
    kPresolveError,
    kSolveError,
    kPostsolveError,
    kModelEmpty,
    kOptimal,
    kInfeasible,
    kUnboundedOrInfeasible,
    kUnbounded,
    kObjectiveBound,
    kObjectiveTarget,
    kTimeLimit,
    kIterationLimit,
    kUnknown,
    kSolutionLimit,
    kInterrupt,
    kMemoryLimit
}

public enum HighsIntegrality
{
    kContinuous = 0,
    kInteger = 1,
    kSemiContinuous = 2,
    kSemiInteger = 3,
    kImplicitInteger = 4,
}

public class HighsModel
{
    public HighsObjectiveSense sense;
    public double[] colcost;
    public double offset;
    public double[] collower;
    public double[] colupper;
    public double[] rowlower;
    public double[] rowupper;
    public HighsMatrixFormat a_format;
    public int[] astart;
    public int[] aindex;
    public double[] avalue;
    public int[] highs_integrality;
    public HessianFormat? q_format;
    public int[] qstart;
    public int[] qindex;
    public double[] qvalue;

    public HighsModel()
    {

    }

    public HighsModel(double[] colcost, double[] collower, double[] colupper, 
        double[] rowlower, double[] rowupper, int[] astart, int[] aindex, 
        double[] avalue, int[] highs_integrality = null, double offset = 0, 
        HighsMatrixFormat a_format = HighsMatrixFormat.kRowwise, 
        HighsObjectiveSense sense = HighsObjectiveSense.kMinimize, 
        HessianFormat q_format = HessianFormat.kTriangular, int[]? qstart = null, 
        int[]? qindex = null, double[]? qvalue = null)
    {
        this.colcost = colcost;
        this.collower = collower;
        this.colupper = colupper;
        this.rowlower = rowlower;
        this.rowupper = rowupper;
        this.astart = astart;
        this.aindex = aindex;
        this.avalue = avalue;
        this.offset = offset;
        this.a_format = a_format;
        this.sense = sense;
        this.highs_integrality = highs_integrality;
        this.qstart = qstart;
        this.qindex = qindex;
        this.qvalue = qvalue;
        this.q_format = q_format;
    }
}

public class HighsSolution
{
    public double[] colvalue;
    public double[] coldual;
    public double[] rowvalue;
    public double[] rowdual;

    public HighsSolution(int numcol, int numrow)
    {
        this.colvalue = new double[numcol];
        this.coldual = new double[numcol];
        this.rowvalue = new double[numrow];
        this.rowdual = new double[numrow];
    }

    public HighsSolution(double[] colvalue, double[] coldual, double[] rowvalue, double[] rowdual)
    {
        this.colvalue = colvalue;
        this.coldual = coldual;
        this.rowvalue = rowvalue;
        this.rowdual = rowdual;
    }
}

public class HighsBasis
{
    public HighsBasisStatus[] colbasisstatus;
    public HighsBasisStatus[] rowbasisstatus;

    public HighsBasis(int numcol, int numrow)
    {
        this.colbasisstatus = new HighsBasisStatus[numcol];
        this.rowbasisstatus = new HighsBasisStatus[numrow];
    }

    public HighsBasis(HighsBasisStatus[] colbasisstatus, HighsBasisStatus[] rowbasisstatus)
    {
        this.colbasisstatus = colbasisstatus;
        this.rowbasisstatus = rowbasisstatus;
    }
}

public class HighsSolver : IDisposable
{
    private IntPtr highs;

    private bool _disposed;

    private const string highslibname = "highs";

    [DllImport(highslibname)]
    private static extern int Highs_call(
        Int32 numcol,
        Int32 numrow,
        Int32 numnz,
        double[] colcost,
        double[] collower,
        double[] colupper,
        double[] rowlower,
        double[] rowupper,
        int[] astart,
        int[] aindex,
        double[] avalue,
        double[] colvalue,
        double[] coldual,
        double[] rowvalue,
        double[] rowdual,
        int[] colbasisstatus,
        int[] rowbasisstatus,
        ref int modelstatus);

    [DllImport(highslibname)]
    private static extern IntPtr Highs_create();

    [DllImport(highslibname)]
    private static extern void Highs_destroy(IntPtr highs);

    [DllImport(highslibname)]
    private static extern int Highs_run(IntPtr highs);

    [DllImport(highslibname)]
    private static extern int Highs_readModel(IntPtr highs, string filename);

    [DllImport(highslibname)]
    private static extern int Highs_writeModel(IntPtr highs, string filename);

    [DllImport(highslibname)]
    private static extern int Highs_writePresolvedModel(IntPtr highs, string filename);

    [DllImport(highslibname)]
    private static extern int Highs_writeSolutionPretty(IntPtr highs, string filename);

    [DllImport(highslibname)]
    private static extern int Highs_getInfinity(IntPtr highs);

    [DllImport(highslibname)]
    private static extern int Highs_passLp(
        IntPtr highs,
        int numcol,
        int numrow,
        int numnz,
        int aformat,
        int sense,
        double offset,
        double[] colcost,
        double[] collower,
        double[] colupper,
        double[] rowlower,
        double[] rowupper,
        int[] astart,
        int[] aindex,
        double[] avalue);

    [DllImport(highslibname)]
    private static extern int Highs_passMip(
        IntPtr highs,
        int numcol,
        int numrow,
        int numnz,
        int aformat,
        int sense,
        double offset,
        double[] colcost,
        double[] collower,
        double[] colupper,
        double[] rowlower,
        double[] rowupper,
        int[] astart,
        int[] aindex,
        double[] avalue,
        int[] highs_integrality);

    [DllImport(highslibname)]
    private static extern int Highs_passModel(
        IntPtr highs,
        int numcol,
        int numrow,
        int numnz,
        int qnumnz,
        int aformat,
        int qformat,
        int sense,
        double offset,
        double[] colcost,
        double[] collower,
        double[] colupper,
        double[] rowlower,
        double[] rowupper,
        int[] astart,
        int[] aindex,
        double[] avalue,
        int[] qstart,
        int[] qindex,
        double[] qvalue,
        int[] highs_integrality);

    [DllImport(highslibname)]
    private static extern int Highs_passHessian(
        IntPtr highs,
        int dim,
        int numnz,
        int q_format,
        int[] qstart,
        int[] qindex,
        double[] qvalue);

    [DllImport(highslibname)]
    private static extern int Highs_setOptionValue(IntPtr highs, string option, string value);

    [DllImport(highslibname)]
    private static extern int Highs_setBoolOptionValue(IntPtr highs, string option, int value);

    [DllImport(highslibname)]
    private static extern int Highs_setIntOptionValue(IntPtr highs, string option, int value);

    [DllImport(highslibname)]
    private static extern int Highs_setDoubleOptionValue(IntPtr highs, string option, double value);

    [DllImport(highslibname)]
    private static extern int Highs_setStringOptionValue(IntPtr highs, string option, string value);

    [DllImport(highslibname)]
    private static extern int Highs_getBoolOptionValue(IntPtr highs, string option, out int value);

    [DllImport(highslibname)]
    private static extern int Highs_getIntOptionValue(IntPtr highs, string option, out int value);

    [DllImport(highslibname)]
    private static extern int Highs_getDoubleOptionValue(IntPtr highs, string option, out double value);

    [DllImport(highslibname)]
    private static extern int Highs_getStringOptionValue(IntPtr highs, string option, [Out] StringBuilder value);

    [DllImport(highslibname)]
    private static extern int Highs_getSolution(IntPtr highs, double[] colvalue, double[] coldual, double[] rowvalue, double[] rowdual);

    [DllImport(highslibname)]
    private static extern int Highs_getNumCol(IntPtr highs);

    [DllImport(highslibname)]
    private static extern int Highs_getNumRow(IntPtr highs);

    [DllImport(highslibname)]
    private static extern int Highs_getNumNz(IntPtr highs);

    [DllImport(highslibname)]
    private static extern int Highs_getHessianNumNz(IntPtr highs);

    [DllImport(highslibname)]
    private static extern int Highs_getBasis(IntPtr highs, int[] colstatus, int[] rowstatus);

    [DllImport(highslibname)]
    private static extern double Highs_getObjectiveValue(IntPtr highs);

    [DllImport(highslibname)]
    private static extern int Highs_getIterationCount(IntPtr highs);

    [DllImport(highslibname)]
    private static extern int Highs_getModelStatus(IntPtr highs);

    [DllImport(highslibname)]
    private static extern int Highs_addRow(IntPtr highs, double lower, double upper, int num_new_nz, int[] indices, double[] values);

    [DllImport(highslibname)]
    private static extern int Highs_addRows(
        IntPtr highs,
        int num_new_row,
        double[] lower,
        double[] upper,
        int num_new_nz,
        int[] starts,
        int[] indices,
        double[] values);

    [DllImport(highslibname)]
    private static extern int Highs_addCol(
        IntPtr highs,
        double cost,
        double lower,
        double upper,
        int num_new_nz,
        int[] indices,
        double[] values);

    [DllImport(highslibname)]
    private static extern int Highs_addCols(
        IntPtr highs,
        int num_new_col,
        double[] costs,
        double[] lower,
        double[] upper,
        int num_new_nz,
        int[] starts,
        int[] indices,
        double[] values);

    [DllImport(highslibname)]
    private static extern int Highs_changeObjectiveSense(IntPtr highs, int sense);

    [DllImport(highslibname)]
    private static extern int Highs_changeColCost(IntPtr highs, int col, double cost);

    [DllImport(highslibname)]
    private static extern int Highs_changeColsCostBySet(IntPtr highs, int num_set_entries, int[] set, double[] cost);

    [DllImport(highslibname)]
    private static extern int Highs_changeColsCostByMask(IntPtr highs, int[] mask, double[] cost);

    [DllImport(highslibname)]
    private static extern int Highs_changeColBounds(IntPtr highs, int col, double lower, double upper);

    [DllImport(highslibname)]
    private static extern int Highs_changeColsBoundsByRange(IntPtr highs, int from_col, int to_col, double[] lower, double[] upper);

    [DllImport(highslibname)]
    private static extern int Highs_changeColsBoundsBySet(IntPtr highs, int num_set_entries, int[] set, double[] lower, double[] upper);

    [DllImport(highslibname)]
    private static extern int Highs_changeColsBoundsByMask(IntPtr highs, int[] mask, double[] lower, double[] upper);

    [DllImport(highslibname)]
    private static extern int Highs_changeRowBounds(IntPtr highs, int row, double lower, double upper);

    [DllImport(highslibname)]
    private static extern int Highs_changeRowsBoundsBySet(IntPtr highs, int num_set_entries, int[] set, double[] lower, double[] upper);

    [DllImport(highslibname)]
    private static extern int Highs_changeRowsBoundsByMask(IntPtr highs, int[] mask, double[] lower, double[] upper);

    [DllImport(highslibname)]
    private static extern int Highs_changeColsIntegralityByRange(IntPtr highs, int from_col, int to_col, int[] integrality);

    [DllImport(highslibname)]
    private static extern int Highs_changeCoeff(IntPtr highs, int row, int col, double value);

    [DllImport(highslibname)]
    private static extern int Highs_deleteColsByRange(IntPtr highs, int from_col, int to_col);

    [DllImport(highslibname)]
    private static extern int Highs_deleteColsBySet(IntPtr highs, int num_set_entries, int[] set);

    [DllImport(highslibname)]
    private static extern int Highs_deleteColsByMask(IntPtr highs, int[] mask);

    [DllImport(highslibname)]
    private static extern int Highs_deleteRowsByRange(IntPtr highs, int from_row, int to_row);

    [DllImport(highslibname)]
    private static extern int Highs_deleteRowsBySet(IntPtr highs, int num_set_entries, int[] set);

    [DllImport(highslibname)]
    private static extern int Highs_deleteRowsByMask(IntPtr highs, int[] mask);

    [DllImport(highslibname)]
    private static extern int Highs_getDoubleInfoValue(IntPtr highs, string info, out double value);

    [DllImport(highslibname)]
    private static extern int Highs_getIntInfoValue(IntPtr highs, string info, out int value);

    [DllImport(highslibname)]
    private static extern int Highs_getInt64InfoValue(IntPtr highs, string info, out long value);

    [DllImport(highslibname)]
    private static extern int Highs_setSolution(IntPtr highs, double[] col_value, double[] row_value, double[] col_dual, double[] row_dual);

    [DllImport(highslibname)]
    private static extern int Highs_getColsByRange(
        IntPtr highs,
        int from_col,
        int to_col,
        ref int num_col,
        double[] costs,
        double[] lower,
        double[] upper,
        ref int num_nz,
        int[] matrix_start,
        int[] matrix_index,
        double[] matrix_value);

    [DllImport(highslibname)]
    private static extern int Highs_getColsBySet(
        IntPtr highs,
        int num_set_entries,
        int[] set,
        ref int num_col,
        double[] costs,
        double[] lower,
        double[] upper,
        ref int num_nz,
        int[] matrix_start,
        int[] matrix_index,
        double[] matrix_value);

    [DllImport(highslibname)]
    private static extern int Highs_getColsByMask(
        IntPtr highs,
        int[] mask,
        ref int num_col,
        double[] costs,
        double[] lower,
        double[] upper,
        ref int num_nz,
        int[] matrix_start,
        int[] matrix_index,
        double[] matrix_value);

    [DllImport(highslibname)]
    private static extern int Highs_getRowsByRange(
        IntPtr highs,
        int from_row,
        int to_row,
        ref int num_row,
        double[] lower,
        double[] upper,
        ref int num_nz,
        int[] matrix_start,
        int[] matrix_index,
        double[] matrix_value);

    [DllImport(highslibname)]
    private static extern int Highs_getRowsBySet(
        IntPtr highs,
        int num_set_entries,
        int[] set,
        ref int num_row,
        double[] lower,
        double[] upper,
        ref int num_nz,
        int[] matrix_start,
        int[] matrix_index,
        double[] matrix_value);

    [DllImport(highslibname)]
    private static extern int Highs_getRowsByMask(
        IntPtr highs,
        int[] mask,
        ref int num_row,
        double[] lower,
        double[] upper,
        ref int num_nz,
        int[] matrix_start,
        int[] matrix_index,
        double[] matrix_value);

    [DllImport(highslibname)]
    private static extern int Highs_getBasicVariables(IntPtr highs, int[] basic_variables);

    [DllImport(highslibname)]
    private static extern int Highs_getBasisInverseRow(IntPtr highs, int row, double[] row_vector, ref int row_num_nz, int[] row_indices);

    [DllImport(highslibname)]
    private static extern int Highs_getBasisInverseCol(IntPtr highs, int col, double[] col_vector, ref int col_num_nz, int[] col_indices);

    [DllImport(highslibname)]
    private static extern int Highs_getBasisSolve(
        IntPtr highs,
        double[] rhs,
        double[] solution_vector,
        ref int solution_num_nz,
        int[] solution_indices);

    [DllImport(highslibname)]
    private static extern int Highs_getBasisTransposeSolve(
        IntPtr highs,
        double[] rhs,
        double[] solution_vector,
        ref int solution_nz,
        int[] solution_indices);

    [DllImport(highslibname)]
    private static extern int Highs_getReducedRow(IntPtr highs, int row, double[] row_vector, ref int row_num_nz, int[] row_indices);

    [DllImport(highslibname)]
    private static extern int Highs_getReducedColumn(IntPtr highs, int col, double[] col_vector, ref int col_num_nz, int[] col_indices);

    [DllImport(highslibname)]
    private static extern int Highs_clearModel(IntPtr highs);

    [DllImport(highslibname)]
    private static extern int Highs_clearSolver(IntPtr highs);

    [DllImport(highslibname)]
    private static extern int Highs_passColName(IntPtr highs, int col, string name);

    [DllImport(highslibname)]
    private static extern int Highs_passRowName(IntPtr highs, int row, string name);

    [DllImport(highslibname)]
    private static extern int Highs_writeOptions(IntPtr highs, string filename);

    [DllImport(highslibname)]
    private static extern int Highs_writeOptionsDeviations(IntPtr highs, string filename);

    public static HighsStatus call(HighsModel model, ref HighsSolution sol, ref HighsBasis bas, ref HighsModelStatus modelstatus)
    {
        int nc = model.colcost.Length;
        int nr = model.rowlower.Length;
        int nnz = model.avalue.Length;

        int[] colbasstat = new int[nc];
        int[] rowbasstat = new int[nr];

        int modelstate = 0;

        HighsStatus status = (HighsStatus)HighsSolver.Highs_call(
            nc,
            nr,
            nnz,
            model.colcost,
            model.collower,
            model.colupper,
            model.rowlower,
            model.rowupper,
            model.astart,
            model.aindex,
            model.avalue,
            sol.colvalue,
            sol.coldual,
            sol.rowvalue,
            sol.rowdual,
            colbasstat,
            rowbasstat,
            ref modelstate);

        modelstatus = (HighsModelStatus)modelstate;

        bas.colbasisstatus = colbasstat.Select(x => (HighsBasisStatus)x).ToArray();
        bas.rowbasisstatus = rowbasstat.Select(x => (HighsBasisStatus)x).ToArray();

        return status;
    }

    public HighsSolver()
    {
        this.highs = HighsSolver.Highs_create();
    }

    ~HighsSolver()
    {
        this.Dispose(false);
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (this._disposed)
        {
            return;
        }

        HighsSolver.Highs_destroy(this.highs);
        this._disposed = true;
    }

    public HighsStatus run()
    {
        return (HighsStatus)HighsSolver.Highs_run(this.highs);
    }

    public HighsStatus readModel(string filename)
    {
        return (HighsStatus)HighsSolver.Highs_readModel(this.highs, filename);
    }

    public HighsStatus writeModel(string filename)
    {
        return (HighsStatus)HighsSolver.Highs_writeModel(this.highs, filename);
    }

    public HighsStatus writePresolvedModel(string filename)
    {
        return (HighsStatus)HighsSolver.Highs_writePresolvedModel(this.highs, filename);
    }

    public HighsStatus writeSolutionPretty(string filename)
    {
        return (HighsStatus)HighsSolver.Highs_writeSolutionPretty(this.highs, filename);
    }

    public Double getInfinity()
    {
        return (Double)HighsSolver.Highs_getInfinity(this.highs);
    }

    public HighsStatus passModel(HighsModel model)
    {
        int rowlowerLength = (model.rowlower is null) ? 0 : model.rowlower.Length;
        int avalueLength = (model.avalue is null) ? 0 : model.avalue.Length;
        int qvalueLength = (model.qvalue is null) ? 0 : model.qvalue.Length;
        return (HighsStatus)HighsSolver.Highs_passModel(
            this.highs,
            model.colcost.Length,
            rowlowerLength,
            avalueLength,
            qvalueLength,
            (int)model.a_format,
            (int)model.q_format,
            (int)model.sense,
            model.offset,
            model.colcost,
            model.collower,
            model.colupper,
            model.rowlower,
            model.rowupper,
            model.astart,
            model.aindex,
            model.avalue,
            model.qstart,
            model.qindex,
            model.qvalue,
            model.highs_integrality);
    }
    public HighsStatus setOptionValue(string option, string value)
    {
        return (HighsStatus)HighsSolver.Highs_setOptionValue(this.highs, option, value);
    }

    public HighsStatus setStringOptionValue(string option, string value)
    {
        return (HighsStatus)HighsSolver.Highs_setStringOptionValue(this.highs, option, value);
    }

    public HighsStatus setBoolOptionValue(string option, int value)
    {
        return (HighsStatus)HighsSolver.Highs_setBoolOptionValue(this.highs, option, value);
    }

    public HighsStatus setDoubleOptionValue(string option, double value)
    {
        return (HighsStatus)HighsSolver.Highs_setDoubleOptionValue(this.highs, option, value);
    }

    public HighsStatus setIntOptionValue(string option, int value)
    {
        return (HighsStatus)HighsSolver.Highs_setIntOptionValue(this.highs, option, value);
    }

    public HighsStatus getStringOptionValue(string option, out string value)
    {
        var stringBuilder = new StringBuilder();
        var result = (HighsStatus)HighsSolver.Highs_getStringOptionValue(this.highs, option, stringBuilder);
        value = stringBuilder.ToString();
        return result;
    }

    public HighsStatus getBoolOptionValue(string option, out int value)
    {
        return (HighsStatus)HighsSolver.Highs_getBoolOptionValue(this.highs, option, out value);
    }

    public HighsStatus getDoubleOptionValue(string option, out double value)
    {
        return (HighsStatus)HighsSolver.Highs_getDoubleOptionValue(this.highs, option, out value);
    }

    public HighsStatus getIntOptionValue(string option, out int value)
    {
        return (HighsStatus)HighsSolver.Highs_getIntOptionValue(this.highs, option, out value);
    }

    public int getNumCol()
    {
        return HighsSolver.Highs_getNumCol(this.highs);
    }

    public int getNumRow()
    {
        return HighsSolver.Highs_getNumRow(this.highs);
    }

    public int getNumNz()
    {
        return HighsSolver.Highs_getNumNz(this.highs);
    }

    public HighsSolution getSolution()
    {
        int nc = this.getNumCol();
        int nr = this.getNumRow();

        HighsSolution sol = new HighsSolution(nc, nr);
        HighsSolver.Highs_getSolution(this.highs, sol.colvalue, sol.coldual, sol.rowvalue, sol.rowdual);

        return sol;
    }

    public HighsBasis getBasis()
    {
        int nc = this.getNumCol();
        int nr = this.getNumRow();

        int[] colbasstat = new int[nc];
        int[] rowbasstat = new int[nr];

        HighsSolver.Highs_getBasis(this.highs, colbasstat, rowbasstat);
        HighsBasis bas = new HighsBasis(
            colbasstat.Select(x => (HighsBasisStatus)x).ToArray(),
            rowbasstat.Select(x => (HighsBasisStatus)x).ToArray());

        return bas;
    }

    public double getObjectiveValue()
    {
        return HighsSolver.Highs_getObjectiveValue(this.highs);
    }

    public HighsModelStatus GetModelStatus()
    {
        return (HighsModelStatus)HighsSolver.Highs_getModelStatus(this.highs);
    }

    public int getIterationCount()
    {
        return HighsSolver.Highs_getIterationCount(this.highs);
    }

    public HighsStatus addRow(double lower, double upper, int[] indices, double[] values)
    {
        return (HighsStatus)HighsSolver.Highs_addRow(this.highs, lower, upper, indices.Length, indices, values);
    }

    public HighsStatus addRows(double[] lower, double[] upper, int[] starts, int[] indices, double[] values)
    {
        return (HighsStatus)HighsSolver.Highs_addRows(this.highs, lower.Length, lower, upper, indices.Length, starts, indices, values);
    }

    public HighsStatus addCol(double cost, double lower, double upper, int[] indices, double[] values)
    {
        return (HighsStatus)HighsSolver.Highs_addCol(this.highs, cost, lower, upper, indices.Length, indices, values);
    }

    public HighsStatus addCols(double[] costs, double[] lower, double[] upper, int[] starts, int[] indices, double[] values)
    {
        return (HighsStatus)HighsSolver.Highs_addCols(
            this.highs,
            costs.Length,
            costs,
            lower,
            upper,
            indices.Length,
            starts,
            indices,
            values);
    }

    public HighsStatus changeObjectiveSense(HighsObjectiveSense sense)
    {
        return (HighsStatus)HighsSolver.Highs_changeObjectiveSense(this.highs, (int)sense);
    }

    public HighsStatus changeColCost(int col, double cost)
    {
        return (HighsStatus)HighsSolver.Highs_changeColCost(this.highs, col, cost);
    }

    public HighsStatus changeColsCostBySet(int[] cols, double[] costs)
    {
        return (HighsStatus)HighsSolver.Highs_changeColsCostBySet(this.highs, cols.Length, cols, costs);
    }

    public HighsStatus changeColsCostByMask(bool[] mask, double[] cost)
    {
        return (HighsStatus)HighsSolver.Highs_changeColsCostByMask(this.highs, mask.Select(x => x ? 1 : 0).ToArray(), cost);
    }

    public HighsStatus changeColBounds(int col, double lower, double upper)
    {
        return (HighsStatus)HighsSolver.Highs_changeColBounds(this.highs, col, lower, upper);
    }

    public HighsStatus changeColsBoundsByRange(int from, int to, double[] lower, double[] upper)
    {
        return (HighsStatus)HighsSolver.Highs_changeColsBoundsByRange(this.highs, from, to, lower, upper);
    }

    public HighsStatus changeColsBoundsBySet(int[] cols, double[] lower, double[] upper)
    {
        return (HighsStatus)HighsSolver.Highs_changeColsBoundsBySet(this.highs, cols.Length, cols, lower, upper);
    }

    public HighsStatus changeColsBoundsByMask(bool[] mask, double[] lower, double[] upper)
    {
        return (HighsStatus)HighsSolver.Highs_changeColsBoundsByMask(this.highs, mask.Select(x => x ? 1 : 0).ToArray(), lower, upper);
    }

    public HighsStatus changeRowBounds(int row, double lower, double upper)
    {
        return (HighsStatus)HighsSolver.Highs_changeRowBounds(this.highs, row, lower, upper);
    }

    public HighsStatus changeRowsBoundsBySet(int[] rows, double[] lower, double[] upper)
    {
        return (HighsStatus)HighsSolver.Highs_changeRowsBoundsBySet(this.highs, rows.Length, rows, lower, upper);
    }

    public HighsStatus changeRowsBoundsByMask(bool[] mask, double[] lower, double[] upper)
    {
        return (HighsStatus)HighsSolver.Highs_changeRowsBoundsByMask(this.highs, mask.Select(x => x ? 1 : 0).ToArray(), lower, upper);
    }

    public HighsStatus changeColsIntegralityByRange(int from_col, int to_col, HighsIntegrality[] integrality)
    {
        return (HighsStatus)HighsSolver.Highs_changeColsIntegralityByRange(this.highs, from_col, to_col, Array.ConvertAll(integrality, item => (int)item));
    }

    public HighsStatus changeCoeff(int row, int col, double value)
    {
        return (HighsStatus)HighsSolver.Highs_changeCoeff(this.highs, row, col, value);
    }

    public HighsStatus deleteColsByRange(int from, int to)
    {
        return (HighsStatus)HighsSolver.Highs_deleteColsByRange(this.highs, from, to);
    }

    public HighsStatus deleteColsBySet(int[] cols)
    {
        return (HighsStatus)HighsSolver.Highs_deleteColsBySet(this.highs, cols.Length, cols);
    }

    public HighsStatus deleteColsByMask(bool[] mask)
    {
        return (HighsStatus)HighsSolver.Highs_deleteColsByMask(this.highs, mask.Select(x => x ? 1 : 0).ToArray());
    }

    public HighsStatus deleteRowsByRange(int from, int to)
    {
        return (HighsStatus)HighsSolver.Highs_deleteRowsByRange(this.highs, from, to);
    }

    public HighsStatus deleteRowsBySet(int[] rows)
    {
        return (HighsStatus)HighsSolver.Highs_deleteRowsBySet(this.highs, rows.Length, rows);
    }

    public HighsStatus deleteRowsByMask(bool[] mask)
    {
        return (HighsStatus)HighsSolver.Highs_deleteRowsByMask(this.highs, mask.Select(x => x ? 1 : 0).ToArray());
    }

    delegate int HighsGetInfoDelegate<TValue>(IntPtr highs, string infoName, out TValue output);

    private TValue GetValueOrFallback<TValue>(HighsGetInfoDelegate<TValue> highsGetInfoDelegate, string infoName, TValue fallback)
    {
        try
        {
            var status = (HighsStatus)highsGetInfoDelegate(this.highs, infoName, out var value);
            if (status != HighsStatus.kOk)
            {
                return fallback;
            }

            return value;
        }
        catch
        {
            return fallback;
        }
    }

    /// <summary>
    /// Gets the current solution info.
    /// </summary>
    /// <returns>The <see cref="SolutionInfo"/>.</returns>
    public SolutionInfo getInfo()
    {
        // TODO: This object does not contian the "complete" info from the C api. Add further props, if you need them.
        var info = new SolutionInfo()
        {
            MipGap = this.GetValueOrFallback(HighsSolver.Highs_getDoubleInfoValue, "mip_gap", double.NaN),
            DualBound = this.GetValueOrFallback(HighsSolver.Highs_getDoubleInfoValue, "mip_dual_bound", double.NaN),
            ObjectiveValue = this.GetValueOrFallback(HighsSolver.Highs_getDoubleInfoValue, "objective_function_value", double.NaN),
            NodeCount = this.GetValueOrFallback(HighsSolver.Highs_getInt64InfoValue, "mip_node_count", 0L),
            IpmIterationCount = this.GetValueOrFallback(HighsSolver.Highs_getIntInfoValue, "ipm_iteration_count", 0),
            SimplexIterationCount = this.GetValueOrFallback(HighsSolver.Highs_getIntInfoValue, "simplex_iteration_count", 0),
            PdlpIterationCount = this.GetValueOrFallback(HighsSolver.Highs_getIntInfoValue, "pdlp_iteration_count", 0),
            QpIterationCount = this.GetValueOrFallback(HighsSolver.Highs_getIntInfoValue, "qp_iteration_count", 0),
        };
        return info;
    }

    public HighsStatus setSolution(HighsSolution solution)
    {
        return (HighsStatus)HighsSolver.Highs_setSolution(this.highs, solution.colvalue, solution.coldual, solution.rowvalue, solution.rowdual);
    }

    public HighsStatus getBasicVariables(ref int[] basic_variables)
    {
        return (HighsStatus)Highs_getBasicVariables(this.highs, basic_variables);
    }

    public HighsStatus getBasisInverseRow(int row, double[] row_vector, ref int row_num_nz, int[] row_indices)
    {
        return (HighsStatus)Highs_getBasisInverseRow(this.highs, row, row_vector, ref row_num_nz, row_indices);
    }

    public HighsStatus getBasisInverseCol(int col, double[] col_vector, ref int col_num_nz, int[] col_indices)
    {
        return (HighsStatus)Highs_getBasisInverseCol(this.highs, col, col_vector, ref col_num_nz, col_indices);
    }

    public HighsStatus getBasisSolve(double[] rhs, double[] solution_vector, ref int solution_num_nz, int[] solution_indices)
    {
        return (HighsStatus)Highs_getBasisSolve(this.highs, rhs, solution_vector, ref solution_num_nz, solution_indices);
    }

    public HighsStatus getBasisTransposeSolve(double[] rhs, double[] solution_vector, ref int solution_num_nz, int[] solution_indices)
    {
        return (HighsStatus)Highs_getBasisTransposeSolve(this.highs, rhs, solution_vector, ref solution_num_nz, solution_indices);
    }

    public HighsStatus getReducedRow(int row, double[] row_vector, ref int row_num_nz, int[] row_indices)
    {
        return (HighsStatus)Highs_getReducedRow(this.highs, row, row_vector, ref row_num_nz, row_indices);
    }

    public HighsStatus getReducedColumn(int col, double[] col_vector, ref int col_num_nz, int[] col_indices)
    {
        return (HighsStatus)Highs_getReducedColumn(this.highs, col, col_vector, ref col_num_nz, col_indices);
    }

    public HighsStatus clearModel()
    {
        return (HighsStatus)Highs_clearModel(this.highs);
    }

    public HighsStatus clearSolver()
    {
        return (HighsStatus)Highs_clearSolver(this.highs);
    }

    public HighsStatus passColName(int col, string name)
    {
        return (HighsStatus)Highs_passColName(this.highs, col, name);
    }

    public HighsStatus passRowName(int row, string name)
    {
        return (HighsStatus)Highs_passRowName(this.highs, row, name);
    }

    public HighsStatus writeOptions(string filename)
    {
        return (HighsStatus)Highs_writeOptions(this.highs, filename);
    }

    public HighsStatus writeOptionsDeviations(string filename)
    {
        return (HighsStatus)Highs_writeOptionsDeviations(this.highs, filename);
    }
}

/// <summary>
/// The solution info.
/// </summary>
public class SolutionInfo
{
    /// <summary>
    /// Gets or sets the simplex iteration count.
    /// </summary>
    public int SimplexIterationCount { get; set; }

    /// <summary>
    /// Gets or sets the QuadProgram iteration count.
    /// </summary>
    public int QpIterationCount { get; set; }

    /// <summary>
    /// Gets or sets the Interior Point Method (IPM) iteration count.
    /// </summary>
    public int IpmIterationCount { get; set; }

    /// <summary>
    /// Gets or sets the PDLP iteration count.
    /// </summary>
    public int PdlpIterationCount { get; set; }

    /// <summary>
    /// Gets or sets the MIP gap.
    /// </summary>
    public double MipGap { get; set; }

    /// <summary>
    /// Gets or sets the best dual bound.
    /// </summary>
    public double DualBound { get; set; }

    /// <summary>
    /// Gets or sets the MIP node count.
    /// </summary>
    public long NodeCount { get; set; }

    /// <summary>
    /// Gets or sets the objective value.
    /// </summary>
    public double ObjectiveValue { get; set; }
}

}

jajhall commented 2 weeks ago

Thank you for all your contribution since yesterday. I have not (now?) completed and integration and tested all examples provided by Matlab here Quadratic programming - MATLAB quadprog (mathworks.com)https://www.mathworks.com/help/optim/ug/quadprog.html And gotten the same result.

Great, that's very good to know

I feel that LP in highs is just a special case of QP without Hessian (I am not sure I am correct since QP in Highs can not handle integer variable).

Not really, HiGHS is an LP/MIP solver that's got a QP solver bolted onto it. Hence the HighsModel class consists of a HighsLp instance and a HighsHessian instance.

Attached here is the copy of my modifications to "highs_csharp_api.cs" file. I have modified it to handle cases where Q might not be given, or the As of the inequality/equality might not be given. And added the "qp_Iteration_Count" to the info. Also, I set the default Matrixformat to "kRowwise" I have also gotten rid of the passLp and passMip, I am now only having passModel. So my QP and LP use of the highs now look very similar. Because in my application, as I am indexing the number of wells, and the facility the flow passes through, the equalities and inequalites came naturally in compressed row form.

Thanks, I'll have a look, but "highs_csharp_api.cs" must be backwards compatible so that code that ran before isn't broken - until we release HiGHS v2.0

Note: We are planning to make our Mathematics Library open and freely available to everyone by the end of October. Is there anything we need to do as regards with our use of Highs?

No, the MIT license means that you can use HiGHS without giving any acknowledgement. Of course, it would be nice if you do.

LatonCypher commented 2 weeks ago

Oh. Ok. Thanks for the clarification.


From: Julian Hall @.> Sent: Thursday, August 29, 2024 4:44 PM To: ERGO-Code/HiGHS @.> Cc: Lateef kareem @.>; Mention @.> Subject: Re: [ERGO-Code/HiGHS] Enable QP handling in C# and tidy up the use of HighsModel (Issue #1900)

Thank you for all your contribution since yesterday. I have not (now?) completed and integration and tested all examples provided by Matlab here Quadratic programming - MATLAB quadprog (mathworks.com)https://www.mathworks.com/help/optim/ug/quadprog.html And gotten the same result.

Great, that's very good to know

I feel that LP in highs is just a special case of QP without Hessian (I am not sure I am correct since QP in Highs can not handle integer variable).

Not really, HiGHS is an LP/MIP solver that's got a QP solver bolted onto it. Hence the HighsModel class consists of a HighsLp instance and a HighsHessian instance.

Attached here is the copy of my modifications to "highs_csharp_api.cs" file. I have modified it to handle cases where Q might not be given, or the As of the inequality/equality might not be given. And added the "qp_Iteration_Count" to the info. Also, I set the default Matrixformat to "kRowwise" I have also gotten rid of the passLp and passMip, I am now only having passModel. So my QP and LP use of the highs now look very similar. Because in my application, as I am indexing the number of wells, and the facility the flow passes through, the equalities and inequalites came naturally in compressed row form.

Thanks, I'll have a look, but "highs_csharp_api.cs" must be backwards compatible so that code that ran before isn't broken - until we release HiGHS v2.0

Note: We are planning to make our Mathematics Library open and freely available to everyone by the end of October. Is there anything we need to do as regards with our use of Highs?

No, the MIT license means that you can use HiGHS without giving any acknowledgement. Of course, it would be nice if you do.

— Reply to this email directly, view it on GitHubhttps://github.com/ERGO-Code/HiGHS/issues/1900#issuecomment-2318191064, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AW46QB6WSLURJYXCBUCIE5LZT46XTAVCNFSM6AAAAABNIOFTHCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGMJYGE4TCMBWGQ. You are receiving this because you were mentioned.Message ID: @.***>

jajhall commented 1 week ago

@LatonCypher Thanks for your file, but it breaks the API in several ways, so we can't use it.

Note that passHessian will always be necessary since an LP can be modified by adding columns, but there are no corresponding methods to update the Hessian.

We will be sure to revise highs_csharp_api.cs to incorporate QPs in a way that's consistent with the C++ API when we prepare v2.0 of HiGHS.