[Coin-discuss] Questions about OSI - 3

Laszlo Ladanyi ladanyi at us.ibm.com
Mon Jul 9 08:52:40 EDT 2001


Hello.

If you please, we'd need to further discuss some of your answers.
First the minor points.

>  > Further, for the same reason we suggest to change the methods
>  > <code>
>  > virtual bool setIntParam(OsiIntParam key, int value)
>  > virtual bool setDblParam(OsiDblParam key, double value)
>  > virtual bool getIntParam(OsiIntParam key, int& value)
>  > virtual bool getDblParam(OsiDblParam key, double& value)
>  >   <\code>
>  > in
>  > <code>
>  > virtual bool setIntParam(int key, int value)
>  > virtual bool setDblParam(int key, double value)
>  > virtual bool getIntParam(int key, int& value)
>  > virtual bool getDblParam(int key, double& value)
>  > <\code>
>  >
>  > This would allow derived solvers to re-use the methods for setting/getting
>  > their specific parameters together with the "standard" ones.
>
>  Could you explain this a bit more? If I understand your point correctly
>  then you suggest this change so that the solvers could just simply pass
>  the arguments to their internal routines without having to switch. However,
>  the value of key can be different for different solvers

No, that was not the idea.

The point is: if you define enums OsiIntParam and OsiDblParam, you restrict
from the beginning the set of parameters that can be set/read to only those
for which you provided values in the enum.

An implementation can have other parameters, say something like the pricing
rules. Of course these parameters won't be among the ones of the base OSI
class, but it makes sense to have them available in the specialized
interface of that particular implementation. If you know that you are
dealing with an object of that particular class, you can set those
parameters.

Now, the question is: how is that, in the interface, you set those
parameters?

You could have [set/get]Special[Int/Dbl]Param() to set them. That would
work, and it is the only way you can do it if you define

	virtual bool setIntParam(OsiIntParam key, int value)

But if you

- still define the Osi[Int/Dbl]Param enums,

- define the methods like

	virtual bool setIntParam(int key, int value)

you have another - and, in our opinion, better - alternative.
Quite simply, you can use the same [set/get]***Param routines that
handle the parameters for the base class. You can do that because you
can always use an enum where an int is required. So, if you pass as
`key' an Osi***Param, the method will set/get one of the standard
parameters. But you can pass other values, different from those, and
use the same methods to set implementation-specific paramters.

There is also a nice way for doing this "extending" the enum.
Assume your last OsiIntParam is `LastIntParam', and you want to define
an OsiCplex class which has some other parameters to set.

What you can do is

	enum OsiCplexIntParam { FirstCPXPar = LastIntParam + 1,
	                        SecondCPXPar ,
				ThirdCPXPar ...
				}

In this way you can do things like

	setIntParam( LastIntParam , 10 );
	setIntParam( ThirdCPXParam , 3 );

i.e., use the same set/get methods for setting both the parameters
common to all solvers (those in the abstract class) and those specific
to one implementation, avoiding to littering the interface with at least
four new methods.

>It should be specified in the interface how things are done

Definitely, it should ... :-)

>As for deletion I always assumed that solvers compact the matrix

We do have one solver which moves the last row in place of the deleted
one and leaves everything else untouched. However, as long as the
semantic is clearly stated in the interface we can accept everything.

>  > Proposal 1 - Extend directly the class OsiSolverInterface. All the
>  > "quadratic" methods have a default implementation, so any class derived
>  > from OsiSolverInterface can be left unchanged.

...

>  > Proposal 2 - Derive a new class OsiQPSolverInteface from OsiSolverInterface
>  > with the "quadratic" methods.
>
>  Actually, the same question arose already and if I remember well, we
>  decided to derive a new class. What's more we were thinking on deriving
>  separate classes for simplex based on non-simplex based LP solvers and
>  maybe a higher level class for IP solvers.

This point is really critical for us, so please allow me to insist on it.

I strongly favour the solution 1, or possibly the solution 3 that you
sketch in your answer (if i understood it correctly). The solution 2
has some huge disadvantages, from our viewpoint, that I need to point
to your attention.

If we use solution 1, everybody who has a derived class from
OsiSolverInterface will need to do nothing to adapt to the new setting.
If I need a solver with the quadratic part, what I can do is

- take one implementation with a solver that is also a QP solver, say
   Cplex, already available

- add the few parts for implementing the QP-related methods

- give back the class to its original mantainer, who will afterwards
   take care of it.

In this way, the work I do is kept.

If we use solution 2 instead, what I need to do is

- take one implementation with a solver that is also a QP solver, say
   Cplex, already available

- phisically copy all the code in a new class CplexQPSolver deriving from
   OSIQPSolverInterface

- do the modifications

After that, this will be my own class and I'll be responsible for mantaining
it. I won't be able to simply derive from the existing, say, OsiCplexSolver
because this would require that OsiSolverInterface be a virtual base class of
both OsiCplexSolver and OsiQPSolverInterface, which is not. So there will be
two separate OsiCplexSolver and OsiQPSolver, mantained by two separate
groups, although initially the differences will be minimal. If I stop
having time and resources for mantaining CplexQPSolver, all the work I've
done will go to waste. And anyway I won't be able to automatically benefit
from the improvements in OsiCplexSolver that the original mantainer does.

We don't think that this solution is efficient in terms of software
development. Quite frankly, we don't know if we are going to make this
development effort if there are so little chances that our work will be
beneficial to anybody else.

What we think is that deriving an OsiQPSolverInterface is a departure from
the general idea of your current interface, and should probably be avoided.
Or, better, you might have to take a fundamental decision between two
different models of interface.

Model A: The OsiSolverInterface contains all the methods for all possible
          kinds of solvers we can envision (LP, QP, MIP, Network ...). All
          the methods are structured in such a way that if a solver does not
          support some of the features, it simply has to avoid implementing
          the corresponding methods (of which an implementation is provided
          that just throws an exception).

Model B: There is a GenericOsiSolverInterface which contains only the
          methods that are common to all solvers. Then there are, say,
           OsiLPSolverInterface
	  OsiQPSolverInterface
	  OsiMIPSolverInterface
	 which all derive *virtual* from GenericOsiSolverInterface.
          Maybe OsiLPSolverInterface could have other derived classes
          like OsiLPSimplexSolverInterface and OsiLPIPSolverInterface.
          Then, all the solvers derive from *one or more* of these classes.
          For instance, Cplex would derive from them all. A pure LP would
          derive from some of the LP*Solver. And so on.

At this point your interface is following Model A, but - if we understood
correctly - you are thinking to switching to Model B. Maybe Model B should
indeed be preferred, but that's not the point right now. Our (humble)
opinion is that solution 2 is an hybrid between the two of which we see no
advantages. So, we would like, if possible, to continue the discussion on
this subject until we can understend your motivations for the choice.

Of course, we perfectly see that changing the interface of the base class
is something that should be avoided as much as possible in a distributed
open source effort. However, going with our proposal 1 would not harm the
existing codebase, and would make us more confident that our development
work won't be wasted.

We are more then ready to try to find a compromise between these conflicting
needs. For instance, we could start with solution 2 as you suggest, and
when our derived class is ready and tested we could migrate the methods to
the OsiSolverInterface (assuming you keep on following Model A). This would
require more work than starting with solution 1 directly, but if it makes
the thing feasible, we could do that.

Sorry for the long and complicated e-mail, but we reall need to sort these
things out before starting to commit to the real implementation. Anyway, we
think that these design decisions are very crucial, and need to be carefully
pondered. We do believe that you did, so we'd like to understend better what
you points are.

				Best Regards

				Antonio Frangioni




More information about the Coin-discuss mailing list