001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.math4.optim.nonlinear.scalar;
018
019import java.util.ArrayList;
020import java.util.Collections;
021import java.util.Comparator;
022import java.util.List;
023
024import org.apache.commons.math4.exception.NotStrictlyPositiveException;
025import org.apache.commons.math4.exception.NullArgumentException;
026import org.apache.commons.math4.optim.BaseMultiStartMultivariateOptimizer;
027import org.apache.commons.math4.optim.PointValuePair;
028import org.apache.commons.math4.random.RandomVectorGenerator;
029
030/**
031 * Multi-start optimizer.
032 *
033 * This class wraps an optimizer in order to use it several times in
034 * turn with different starting points (trying to avoid being trapped
035 * in a local extremum when looking for a global one).
036 *
037 * @since 3.0
038 */
039public class MultiStartMultivariateOptimizer
040    extends BaseMultiStartMultivariateOptimizer<PointValuePair> {
041    /** Underlying optimizer. */
042    private final MultivariateOptimizer optimizer;
043    /** Found optima. */
044    private final List<PointValuePair> optima = new ArrayList<>();
045
046    /**
047     * Create a multi-start optimizer from a single-start optimizer.
048     *
049     * @param optimizer Single-start optimizer to wrap.
050     * @param starts Number of starts to perform.
051     * If {@code starts == 1}, the result will be same as if {@code optimizer}
052     * is called directly.
053     * @param generator Random vector generator to use for restarts.
054     * @throws NullArgumentException if {@code optimizer} or {@code generator}
055     * is {@code null}.
056     * @throws NotStrictlyPositiveException if {@code starts < 1}.
057     */
058    public MultiStartMultivariateOptimizer(final MultivariateOptimizer optimizer,
059                                           final int starts,
060                                           final RandomVectorGenerator generator)
061        throws NullArgumentException,
062        NotStrictlyPositiveException {
063        super(optimizer, starts, generator);
064        this.optimizer = optimizer;
065    }
066
067    /**
068     * {@inheritDoc}
069     */
070    @Override
071    public PointValuePair[] getOptima() {
072        Collections.sort(optima, getPairComparator());
073        return optima.toArray(new PointValuePair[0]);
074    }
075
076    /**
077     * {@inheritDoc}
078     */
079    @Override
080    protected void store(PointValuePair optimum) {
081        optima.add(optimum);
082    }
083
084    /**
085     * {@inheritDoc}
086     */
087    @Override
088    protected void clear() {
089        optima.clear();
090    }
091
092    /**
093     * @return a comparator for sorting the optima.
094     */
095    private Comparator<PointValuePair> getPairComparator() {
096        return new Comparator<PointValuePair>() {
097            /** {@inheritDoc} */
098            @Override
099            public int compare(final PointValuePair o1,
100                               final PointValuePair o2) {
101                if (o1 == null) {
102                    return (o2 == null) ? 0 : 1;
103                } else if (o2 == null) {
104                    return -1;
105                }
106                final double v1 = o1.getValue();
107                final double v2 = o2.getValue();
108                return (optimizer.getGoalType() == GoalType.MINIMIZE) ?
109                    Double.compare(v1, v2) : Double.compare(v2, v1);
110            }
111        };
112    }
113}