package com.tesora.dve.tools.aitemplatebuilder; /* * #%L * Tesora Inc. * Database Virtualization Engine * %% * Copyright (C) 2011 - 2014 Tesora Inc. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import net.sourceforge.jFuzzyLogic.FIS; import net.sourceforge.jFuzzyLogic.FunctionBlock; import net.sourceforge.jFuzzyLogic.rule.Rule; import net.sourceforge.jFuzzyLogic.rule.Variable; import org.apache.commons.lang.math.DoubleRange; import org.apache.log4j.Logger; import com.tesora.dve.common.MathUtils; import com.tesora.dve.common.PEFileUtils; import com.tesora.dve.exceptions.PECodingException; import com.tesora.dve.exceptions.PEException; public abstract class FuzzyLinguisticVariable implements TemplateItem { private static final Logger logger = Logger.getLogger(FuzzyLinguisticVariable.class); private static final Comparator<FuzzyLinguisticVariable> MODEL_SCORE_COMPARATOR = new Comparator<FuzzyLinguisticVariable>() { @Override public int compare(FuzzyLinguisticVariable a, FuzzyLinguisticVariable b) { if ((a == null) || (b == null)) { throw new NullPointerException(); } if (a.getScore() > b.getScore()) { return 1; } else if (a.getScore() < b.getScore()) { return -1; } else { return 0; } } }; protected interface FlvName { public String get(); } private enum Variables implements FlvName { SCORE_FLV_NAME { @Override public String get() { return "desirability"; } }; } private static final String RULE_NAME_PREFIX_SEPARATOR = "_"; private static final String FCL_SCHEMA_FILE_NAME = "DistributionModels.fcl"; private static final DoubleRange VALID_RULE_WEIGHT_RANGE = new DoubleRange(0, 1); private static final double DEFAULT_RULE_WEIGHT = 1.0; private final FunctionBlock ai; public static double toPercent(final double value, final double total) { return 100.0 * (value / total); } public static Comparator<FuzzyLinguisticVariable> getScoreComparator() { return MODEL_SCORE_COMPARATOR; } public static List<FuzzyTableDistributionModel> evaluateDistributionModels(final FuzzyTableDistributionModel... distributionModels) { return evaluateDistributionModels(Collections.EMPTY_MAP, distributionModels); } public static List<FuzzyTableDistributionModel> evaluateDistributionModels(final Map<FlvName, Double> ruleWeights, final FuzzyTableDistributionModel... distributionModels) { final List<FuzzyTableDistributionModel> sortedDistributionModels = Arrays.asList(distributionModels); for (final FuzzyLinguisticVariable distributionModel : sortedDistributionModels) { distributionModel.setRuleWeights(ruleWeights); distributionModel.evaluate(); } Collections.sort(sortedDistributionModels, getScoreComparator()); return sortedDistributionModels; } protected FuzzyLinguisticVariable(final String fclBlockName) throws PECodingException { try (final InputStream fclSchema = PEFileUtils.getResourceStream(FuzzyLinguisticVariable.class, FCL_SCHEMA_FILE_NAME)) { this.ai = FIS.load(fclSchema, false).getFunctionBlock(fclBlockName); if (this.ai == null) { throw new PECodingException("Could not load the Fuzzy Control Language (FCL) specification from '" + FCL_SCHEMA_FILE_NAME + "'"); } } catch (final IOException | PEException e) { logger.error(e.getMessage(), e); throw new PECodingException(e); } } protected void setVariable(final FlvName name, final double value) { ai.setVariable(name.get(), value); } protected void setVariables(final Map<FlvName, Double> variables) { if (variables != null) { for (final Map.Entry<FlvName, Double> var : variables.entrySet()) { this.setVariable(var.getKey(), var.getValue()); } } } protected void setRuleWeights(final Map<FlvName, Double> weights) { if (weights != null) { for (final Map.Entry<FlvName, Double> weight : weights.entrySet()) { this.setWeightOnRule(weight.getKey(), weight.getValue()); } } } /** * Set a given weight on all rules named 'prefix_*'. * * @param ruleWeight * A number from interval [0.0, 1.0] or 1.0 if null. */ protected void setWeightOnRule(final FlvName ruleNamePrefix, final Double ruleWeight) { final double weight = (ruleWeight != null) ? ruleWeight : DEFAULT_RULE_WEIGHT; if (!VALID_RULE_WEIGHT_RANGE.containsDouble(weight)) { throw new PECodingException("Weight (" + weight + ") for rule(s) '" + ruleNamePrefix + "*' is out of range."); } for (final Rule rule : this.ai.getFuzzyRuleBlock(null).getRules()) { if (rule.getName().startsWith(ruleNamePrefix.get().concat(RULE_NAME_PREFIX_SEPARATOR))) { rule.setWeight(weight); } } } public void evaluate() { ai.evaluate(); } public double getScore() { return this.getVariable(Variables.SCORE_FLV_NAME).getLatestDefuzzifiedValue(); } public double getVariableValue(final FlvName name) { return this.getVariable(name).getValue(); } private Variable getVariable(final FlvName name) { return this.ai.getVariable(name.get()); } @Override public String toString() { final StringBuilder value = new StringBuilder(); return value.append(this.getFlvName()).append(" (").append(MathUtils.round(this.getScore(), AiTemplateBuilder.NUMBER_DISPLAY_PRECISION)).append(")") .toString(); } /** * Name of the associated Function Block in the FCL file. */ protected abstract String getFclName(); /** * Name of this FLV object. */ protected abstract String getFlvName(); }