commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Luc Maisonobe <...@spaceroots.org>
Subject Re [Math] About the API of the optimizers
Date Thu, 04 Oct 2012 19:43:47 GMT
[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:".

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.

best regards,
Luc

> 
> Gilles

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


Mime
View raw message