/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2009 by Sun Microsystems, Inc. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ package com.sun.star.comp.Calc.NLPSolver; import com.sun.star.comp.Calc.NLPSolver.dialogs.DummyEvolutionarySolverStatusDialog; import com.sun.star.comp.Calc.NLPSolver.dialogs.EvolutionarySolverStatusUno; import com.sun.star.comp.Calc.NLPSolver.dialogs.IEvolutionarySolverStatusDialog; import com.sun.star.sheet.SolverConstraintOperator; import com.sun.star.uno.XComponentContext; import java.util.ArrayList; import net.adaptivebox.global.BasicBound; import net.adaptivebox.global.RandomGenerator; import net.adaptivebox.goodness.ACRComparator; import net.adaptivebox.goodness.BCHComparator; import net.adaptivebox.goodness.IGoodnessCompareEngine; import net.adaptivebox.knowledge.Library; import net.adaptivebox.knowledge.SearchPoint; import net.adaptivebox.problem.ProblemEncoder; public abstract class BaseEvolutionarySolver extends BaseNLPSolver { public BaseEvolutionarySolver(XComponentContext xContext, String name) { super(xContext, name); registerProperty(m_swarmSize); registerProperty(m_learningCycles); registerProperty(m_guessVariableRange); registerProperty(m_variableRangeThreshold); registerProperty(m_useACRComperator); registerProperty(m_useRandomStartingPoint); registerProperty(m_useStrongerPRNG); registerProperty(m_required); registerProperty(m_tolerance); registerProperty(m_enhancedSolverStatus); } private static class Variable { private final CellMap CellMap; private final int OriginalVariable; private double MinValue; private double MaxValue; private double Granularity; private Variable(CellMap cellMap, int originalVariable) { this.CellMap = cellMap; this.OriginalVariable = originalVariable; this.MinValue = BasicBound.MINDOUBLE; this.MaxValue = BasicBound.MAXDOUBLE; this.Granularity = 0.0; } } private class CalcProblemEncoder extends ProblemEncoder { private final ArrayList m_variables; private final ArrayList m_constraints; private CalcProblemEncoder(ArrayList variables, ArrayList constraints) throws Exception { //m_variableCount variables to solve, target function + constraints to match super(variables.size(), 1 + constraints.size()); m_variables = variables; m_constraints = constraints; double objective = m_maximize ? BasicBound.MAXDOUBLE : BasicBound.MINDOUBLE; setDefaultYAt(0, objective, objective); for (int i = 0; i < constraints.size(); i++) { ExtSolverConstraint constraint = constraints.get(i); switch (constraint.Operator.getValue()) { case SolverConstraintOperator.EQUAL_value: setDefaultYAt(i + 1, constraint.Data, constraint.Data); break; case SolverConstraintOperator.GREATER_EQUAL_value: setDefaultYAt(i + 1, constraint.Data, BasicBound.MAXDOUBLE); break; case SolverConstraintOperator.LESS_EQUAL_value: setDefaultYAt(i + 1, BasicBound.MINDOUBLE, constraint.Data); break; case SolverConstraintOperator.INTEGER_value: setDefaultYAt(i + 1, BasicBound.MINDOUBLE, BasicBound.MAXDOUBLE); break; case SolverConstraintOperator.BINARY_value: setDefaultYAt(i + 1, 0, 1); break; } } for (int i = 0; i < m_variables.size(); i++) { Variable variable = m_variables.get(i); setDefaultXAt(i, variable.MinValue, variable.MaxValue, variable.Granularity); } } @Override protected double calcTargetAt(int index, double[] VX) { if (index == 0) { //calcTargetAt is called in a loop over all functions, so it's //enough to set the variables in the first step only for (int i = 0; i < m_variables.size(); i++) { CellMap variableMap = m_variables.get(i).CellMap; m_variableData[variableMap.Range][variableMap.Row][variableMap.Col] = VX[i]; } for (int i = 0; i < m_cellRangeCount; i++) m_cellRangeData[i].setData(m_variableData[i]); //errors are punished if (m_objectiveCell.getError() != 0) return m_maximize ? BasicBound.MINDOUBLE : BasicBound.MAXDOUBLE; double result = m_objectiveCell.getValue(); if (result >= m_toleratedMin && result <= m_toleratedMax && checkConstraints()) m_toleratedCount++; return result; } else return m_constraints.get(index - 1).getLeftValue(); } } protected CalcProblemEncoder m_problemEncoder; protected Library m_library; protected IGoodnessCompareEngine m_envCompareEngine; protected IGoodnessCompareEngine m_specCompareEngine; protected SearchPoint m_totalBestPoint; protected int m_toleratedCount; protected double m_toleratedMin; protected double m_toleratedMax; private final ArrayList m_variables = new ArrayList(); //properties protected PropertyInfo m_swarmSize = new PropertyInfo("SwarmSize", 70, "Size of Swarm"); protected PropertyInfo m_librarySize = new PropertyInfo("LibrarySize", 210, "Size of Library"); protected PropertyInfo m_learningCycles = new PropertyInfo("LearningCycles", 2000, "Learning Cycles"); private final PropertyInfo m_guessVariableRange = new PropertyInfo("GuessVariableRange", true, "Variable Bounds Guessing"); private final PropertyInfo m_variableRangeThreshold = new PropertyInfo("VariableRangeThreshold", 3.0, "Variable Bounds Threshold (when guessing)"); //to approximate the variable bounds private final PropertyInfo m_useACRComperator = new PropertyInfo("UseACRComparator", false, "Use ACR Comparator (instead of BCH)"); private final PropertyInfo m_useRandomStartingPoint = new PropertyInfo("UseRandomStartingPoint", false, "Use Random starting point"); private final PropertyInfo m_useStrongerPRNG = new PropertyInfo("UseStrongerPRNG", false, "Use a stronger random generator (slower)"); protected PropertyInfo m_required = new PropertyInfo("StagnationLimit", 70, "Stagnation Limit"); protected PropertyInfo m_tolerance = new PropertyInfo("Tolerance", 1e-6, "Stagnation Tolerance"); private final PropertyInfo m_enhancedSolverStatus = new PropertyInfo("EnhancedSolverStatus", true, "Show enhanced solver status"); protected IEvolutionarySolverStatusDialog m_solverStatusDialog; private void prepareVariables(double[][] variableBounds) { m_variables.clear(); for (int i = 0; i < m_variableCount; i++) { Variable var = new Variable(m_variableMap[i], i); var.MinValue = variableBounds[i][0]; var.MaxValue = variableBounds[i][1]; var.Granularity = variableBounds[i][2]; m_variables.add(var); } } @Override protected void initializeSolve() { super.initializeSolve(); if (m_variableCount == 0) { return; } if (m_enhancedSolverStatus.getValue()) m_solverStatusDialog = new EvolutionarySolverStatusUno(m_xContext); else m_solverStatusDialog = new DummyEvolutionarySolverStatusDialog(); //Init: double[][] variableBounds = new double[m_variableCount][3]; //approximate variable bounds for (int i = 0; i < m_variableCount; i++) { if (m_guessVariableRange.getValue()) { double value = m_variableCells[i].getValue(); //0 is a bad starting point, so just pick some other. //That is certainly not optimal but the user should specify //bounds or at least a good starting point anyway. if (value == 0.0) value = 1000; double b1; double b2; if (m_assumeNonNegative.getValue()) { b1 = 0; b2 = value + value * 2 * m_variableRangeThreshold.getValue(); } else { b1 = value + value * m_variableRangeThreshold.getValue(); b2 = value - value * m_variableRangeThreshold.getValue(); } variableBounds[i][0] = Math.min(b1, b2); variableBounds[i][1] = Math.max(b1, b2); } else { //that almost always leads to bad or no solutions at all if (m_assumeNonNegative.getValue()) variableBounds[i][0] = 0.0; else variableBounds[i][0] = BasicBound.MINDOUBLE; variableBounds[i][1] = BasicBound.MAXDOUBLE; } variableBounds[i][2] = 0.0; } //prepare constraints and parse them for variable bounds ArrayList constraints = new ArrayList(); for (int i = 0; i < m_constraintCount; i++) { Double doubleValue; if (m_extConstraints[i].Right != null) doubleValue = null; else doubleValue = m_extConstraints[i].Data; boolean isVariableBound = false; //If it refers to a cell, it has to be treated as constraint, not as //bound. if (m_extConstraints[i].Right == null) { for (int j = 0; j < m_variableCount && !isVariableBound; j++) { if (m_constraints[i].Left.Sheet == super.m_variables[j].Sheet && m_constraints[i].Left.Column == super.m_variables[j].Column && m_constraints[i].Left.Row == super.m_variables[j].Row) { isVariableBound = true; //Therefore we try to use it as bounds for this variable. switch (m_extConstraints[i].Operator.getValue()) { case SolverConstraintOperator.EQUAL_value: if (doubleValue == null) continue; variableBounds[j][0] = doubleValue; variableBounds[j][1] = doubleValue; break; case SolverConstraintOperator.GREATER_EQUAL_value: if (doubleValue == null) continue; variableBounds[j][0] = doubleValue; break; case SolverConstraintOperator.LESS_EQUAL_value: if (doubleValue == null) continue; variableBounds[j][1] = doubleValue; break; case SolverConstraintOperator.INTEGER_value: variableBounds[j][2] = 1.0; break; case SolverConstraintOperator.BINARY_value: variableBounds[j][0] = 0.0; variableBounds[j][1] = 1.0; variableBounds[j][2] = 1.0; break; default: //If it is neither <=, nor =, nor >=, we treat //it as normal constraint. isVariableBound = false; } } } } if (!isVariableBound) { constraints.add(m_extConstraints[i]); } } prepareVariables(variableBounds); try { m_problemEncoder = new CalcProblemEncoder(m_variables, constraints); } catch (Exception e) { m_problemEncoder = null; return; } m_library = new Library(m_librarySize.getValue(), m_problemEncoder); if (m_useRandomStartingPoint.getValue()) { m_totalBestPoint = m_problemEncoder.getEncodedSearchPoint(); } else { m_totalBestPoint = m_problemEncoder.getFreshSearchPoint(); double[] currentValues = new double[m_variables.size()]; for (int i = 0; i < m_variables.size(); i++) currentValues[i] = m_currentParameters[m_variables.get(i).OriginalVariable]; m_totalBestPoint.importLocation(currentValues); m_problemEncoder.evaluate(m_totalBestPoint); } //input the chosen point into the library as reference for the individuals m_library.getSelectedPoint(0).importPoint(m_totalBestPoint); m_solverStatusDialog.setBestSolution(m_totalBestPoint.getObjectiveValue(), checkConstraints()); m_envCompareEngine = new BCHComparator(); m_specCompareEngine = m_useACRComperator.getValue() ? new ACRComparator(m_library, m_learningCycles.getValue()) : new BCHComparator(); RandomGenerator.useStrongerGenerator( m_useStrongerPRNG.getValue() ); } protected void applySolution() { double[] location = m_totalBestPoint.getLocation(); //make sure, the "Integer" variable type is met m_problemEncoder.getDesignSpace().getMappingPoint(location); //get the function value for our optimal point for (int i = 0; i < m_variableCount; i++) { m_variableCells[i].setValue(location[i]); m_currentParameters[i] = location[i]; } m_functionValue = m_objectiveCell.getValue(); } @Override protected void finalizeSolve() { applySolution(); m_success = (m_objectiveCell.getError() == 0 && checkConstraints()); m_solverStatusDialog.setVisible(false); m_solverStatusDialog.dispose(); super.finalizeSolve(); } private boolean checkConstraints() { boolean result = true; for (int i = 0; i < m_constraintCount && result; i++) { if (m_extConstraints[i].Left.getError() == 0) { double value = m_extConstraints[i].getLeftValue(); double targetValue = m_extConstraints[i].Data; switch (m_extConstraints[i].Operator.getValue()) { case SolverConstraintOperator.EQUAL_value: result = value == targetValue; break; case SolverConstraintOperator.GREATER_EQUAL_value: result = value >= targetValue; break; case SolverConstraintOperator.LESS_EQUAL_value: result = value <= targetValue; break; case SolverConstraintOperator.INTEGER_value: result = Math.rint(value) == value; break; case SolverConstraintOperator.BINARY_value: result = (value == 0.0 || value == 1.0); break; } } else { result = false; } } return result; } }