commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Gilles Sadowski <gil...@harfang.homelinux.org>
Subject Re: [Math] About the API of the optimizers
Date Thu, 04 Oct 2012 23:15:42 GMT
On Thu, Oct 04, 2012 at 09:43:47PM +0200, Luc Maisonobe wrote:
> [Sorry for the strange format of this mail, but I managed to lose the
> original message from Gilles, so had to recreate it by copy/paste and
> adding the quotation marks by hand...]
> 
> Gilles wrote:
> > Hello.
> > 
> > This issue https://issues.apache.org/jira/browse/MATH-872 is about a 
> > workaround similar to one already existing for "SimplexOptimizer",
> > i.e. having a specific method (setter) to set data that cannot be
> > passed through the common API ("optimize" method) and should not be
> > specified at construction (because it is of the same "nature" as the
> > parameters passed through "optimize").
> > 
> > I was thinking of improving the situation by modifying the
> > "optimize" method (in interface "BaseMultivariateOptimizer") to
> > become: PointValuePair optimize(int maxEval, FUNC f, GoalType
> > goalType, double[] startPoint, OptimizationData... optData);
> 
> > There is a new array argument, "optData", the elements of which are 
> > instances of classes implementing a new marker interface: interface 
> > OptimizationData {}
> 
> > Concrete optimizer implementations would define which kind of data
> > they need (and in which order they must be supplied in the call to 
> > "optimize"). ["BaseAbstractMultivariateOptimizer" would store those
> > data and provide (protected) access to it (similar to
> > "getStartPoint()").]
> 
> > For "SimplexOptimizer", the "data" is the "AbstractSimplex" class
> > which will just have to implement the "OptimizationData" interface:
> > 
> > -----
> > public class AbstractSimplex implements OptimizationData {
> > //No changes here.
> > }
> > -----
> >
> > And, in "SimplexOptimizer", the "doOptimize" method will
> > retrieve the data, check that it has the appropriate type and then
> > use it as before:
> > 
> > -----
> > private PointValuePair doOptimize() {
> > // ...
> > 
> >   final OptimizationData[] optData = getOptimizationData();
> >   if (optData == null || optData.length == 0) {
> >       throw new NoDataException();
> >       // Or: use default values (?).
> >   }
> >   if (!(optData[0] instanceof AbstractSimplex)) {
> >    throw new
> IllegalOptimizationDataTypeException(optData[0].getClass().getName());
> >   }
> >   final AbstractSimplex simplex = (AbstractSimplex) optData[0];
> > 
> > // etc.
> > }
> > -----
> > We could then get rid of the "setSimplex" method that is not
> > part of the optimizers common API, and the necessary input data would
> > be passed with the call to "optimize", e.g. (in user code):
> > 
> > -----
> > final SimplexOptimizer optim = new SimplexOptimizer(absTol, relTol);
> > final MultivariateFunction func = ...
> > final double[] init =  new double[] { 1, 2, 3, 4 };
> > final PointValuePair sol = optim.optimize(2500, func,
> >                 GoalType.MINIMIZE, init,
> >                 new NelderMeadSimplex(init.length, 0.1));
> > -----
> > [Of course, one drawback is that an illegal type argument is
> > detected only at runtime (but it's also an obvious programming bug
> > that would probably only occur during development, and not in
> > production use).]
> 
> I was ready to say just that when I first read this proposal.
> 
> > 
> > One benefit is that bounds could also be considered as a kind of 
> > "OptimizationData":
> > 
> > -----
> > public class SimpleBounds implements OptimizationData {
> >   private final double[] lo; private final double[] hi;
> > 
> >   public SimpleBounds(double[] lowerBounds, double[] upperBounds) {
> >     lo = lowerBounds.clone();
> >     hi = upperBounds.clone();
> >   }
> > 
> >   public double[] getLo {
> >       return lo.clone();
> >   }
> >   public double[] getHi {
> >      return hi.clone();
> >   }
> > }
> > -----
> > We could thus get rid of the  "BaseMultivariateSimpleBoundsOptimizer"
> > interface.
> > 
> > What do you think?
> 
> I'm not sure what to think. I don't much like these too dynamic settings
> and moving checks to runtime. However, I do agree our current
> implementation with dedicated method that must be called before we call
> the common interface methods is not good either. So I think I just need
> to get used to it and could get convinced almost easily.
> 
> What I clearly don't like in our setting is the complexity of the
> hierarchy with the generics. I have the same reluctance with the solvers
> hierarchy, and was directly hit by both when I needed to had a new
> function type for differentials (see
> <http://mail-archives.apache.org/mod_mbox/commons-dev/201209.mbox/%3C5050B2A3.2050007%40free.fr%3E>
> and
> <http://mail-archives.apache.org/mod_mbox/commons-dev/201208.mbox/%3C50374A8F.6040806%40spaceroots.org%3E>).
> So I would be very happy if we could simplify our hierarchy here and
> mainly remove the top level generics (like BaseOptimizer<PAIR> and
> BaseMultivariateOptimizer<FUNC extends MultivariateFunction> extends
> BaseOptimizer<PointValuePair> and simply have a set of parallel
> non-generics interfaces with fixed signatures, one for each type we
> need. These top level interfaces don't add much value and completely
> prevent to implement several of them in one class, due to type erasuer
> (something we did not notice when we designed this). Their javadoc even
> states "This interface is mainly intended to enforce the internal
> coherence of Commons-Math. Users of the API are advised to base their
> code on the following interfaces:".

I am responsible for the current hierarchy design but it was based on an
earlier one, not much simpler, but with much more duplicated code.
At refactoring the identified goal was to merge all the codes that could be.

The point is that different algorithm have generated different based on
several points:
 * Univariate vs multivariate function
 * Scalar vs sector function
 * "optimize" return type: "PointValuePair" vs "PoinVectorValuePair"

The generics were used to "summarize" all the existing flavours (and push
common features one level up). [Boilerplate code in "Abstract..." classes.]

> A coworker of mine asked me today which interface he should use in the
> signature of a method he was writing and which should take an optimizer
> as an argument. He was puzzled by our hierarchy and did not understand
> which level he should use in his declaration. Even knowing the
> internals, the history, the various implementations and their
> differences, it took me almost an hour to answer his question. So our
> hierarchy really needs to be streamlined.

The user interfaces didn't change I think, and do not refer to generics.
They are
 * DifferentiableMultivariateVectorOptimizer
 * DifferentiableMultivariateOptimizer
 * DifferentiableMultivariateMultiStartOptimizer
 * DifferentiableMultivariateVectorMultiStartOptimizer
 * MultivariateMultiStartOptimizer
 * MultivariateOptimizer
 * UnivariateOptimizer 

[Anything whose names start with "Base..." or "Abstract..." is neither for
users nor for application developers but for developers of concrete algorithms
for CM.]


What do you propose? Wat is streamlining a library?

Gilles

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Mime
View raw message