Return-Path: X-Original-To: apmail-commons-dev-archive@www.apache.org Delivered-To: apmail-commons-dev-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 930609B67 for ; Fri, 5 Oct 2012 06:45:35 +0000 (UTC) Received: (qmail 68386 invoked by uid 500); 5 Oct 2012 06:45:34 -0000 Delivered-To: apmail-commons-dev-archive@commons.apache.org Received: (qmail 67998 invoked by uid 500); 5 Oct 2012 06:45:30 -0000 Mailing-List: contact dev-help@commons.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: "Commons Developers List" Delivered-To: mailing list dev@commons.apache.org Received: (qmail 67948 invoked by uid 99); 5 Oct 2012 06:45:29 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 05 Oct 2012 06:45:29 +0000 X-ASF-Spam-Status: No, hits=0.7 required=5.0 tests=SPF_NEUTRAL X-Spam-Check-By: apache.org Received-SPF: neutral (nike.apache.org: local policy) Received: from [80.67.169.19] (HELO solo.fdn.fr) (80.67.169.19) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 05 Oct 2012 06:45:19 +0000 Received: from lehrin.spaceroots.org (smtp.spaceroots.org [80.67.176.229]) by smtp.fdn.fr (Postfix) with ESMTP id 539ED44159 for ; Fri, 5 Oct 2012 08:44:58 +0200 (CEST) Received: from [127.0.0.1] (lehrin.spaceroots.org [127.0.0.1]) by lehrin.spaceroots.org (Postfix) with ESMTP id D33965F51C for ; Fri, 5 Oct 2012 08:44:57 +0200 (CEST) Message-ID: <506E81E9.40009@free.fr> Date: Fri, 05 Oct 2012 08:44:57 +0200 From: Luc Maisonobe User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:10.0.7) Gecko/20120922 Icedove/10.0.7 MIME-Version: 1.0 To: Commons Developers List Subject: Re: [Math] About the API of the optimizers References: <506DE6F3.6060201@spaceroots.org> <20121004231542.GG24856@dusk.harfang.homelinux.org> In-Reply-To: <20121004231542.GG24856@dusk.harfang.homelinux.org> X-Enigmail-Version: 1.4.1 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit Hi Gilles, Le 05/10/2012 01:15, Gilles Sadowski a �crit : > 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 >> >> and >> ). >> So I would be very happy if we could simplify our hierarchy here and >> mainly remove the top level generics (like BaseOptimizer and >> BaseMultivariateOptimizer extends >> BaseOptimizer 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. Yes, this is exactly my point. These interfaces are not for users, they are for [math] developers. However, they do confuse users who see them and even reading the javadoc don't understand their purpose. They also prevent an implementation to support two interfaces at a time and hence to be smoothly upgraded as we improve our basic users interfaces. > 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? I propose to remove the generics from pure interfaces (this would have to wait for 4.0, of course), and hence reduce the number of layers at least by one, and if possible by more than one. Luc > > Gilles > > --------------------------------------------------------------------- > To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org > For additional commands, e-mail: dev-help@commons.apache.org > > --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org For additional commands, e-mail: dev-help@commons.apache.org