This is an automated email from the ASF dual-hosted git repository. nju_yaho pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/kylin.git
The following commit(s) were added to refs/heads/master by this push: new 03316e2 KYLIN-3314 refactor code for cube planner algorithm 03316e2 is described below commit 03316e2ebf5efed203ef16e2cd27b7541d3ff09d Author: Wang Ken <mingmw...@ebay.com> AuthorDate: Wed Mar 7 16:22:36 2018 +0800 KYLIN-3314 refactor code for cube planner algorithm Signed-off-by: Zhong <nju_y...@apache.org> --- .../algorithm/AbstractRecommendAlgorithm.java | 24 +-- .../cube/cuboid/algorithm/BPUSCalculator.java | 56 ++--- .../kylin/cube/cuboid/algorithm/BenefitPolicy.java | 21 +- .../cube/cuboid/algorithm/CuboidBenefitModel.java | 144 +------------ .../kylin/cube/cuboid/algorithm/CuboidStats.java | 37 ++-- .../cube/cuboid/algorithm/PBPUSCalculator.java | 14 +- .../cube/cuboid/algorithm/SPBPUSCalculator.java | 10 +- .../cuboid/algorithm/generic/BitsChromosome.java | 138 ++++++------ .../algorithm/generic/BitsChromosomeHelper.java | 120 +++++++++++ .../algorithm/generic/{lib => }/BitsMutation.java | 28 ++- ...ntCrossover.java => BitsOnePointCrossover.java} | 55 ++--- .../generic/CombinedStoppingCondition.java | 4 +- .../cuboid/algorithm/generic/CuboidEncoder.java | 44 ---- .../cuboid/algorithm/generic/GeneticAlgorithm.java | 237 ++++----------------- .../algorithm/generic/RouletteWheelSelection.java | 13 +- .../cuboid/algorithm/generic/lib/Chromosome.java | 106 --------- .../generic/lib/ChromosomeMismatchException.java | 48 ----- .../algorithm/generic/lib/ChromosomePair.java | 69 ------ .../algorithm/generic/lib/CrossoverPolicy.java | 39 ---- .../generic/lib/ElitisticListPopulation.java | 119 ----------- .../cube/cuboid/algorithm/generic/lib/Fitness.java | 35 --- .../generic/lib/FixedGenerationCount.java | 73 ------- .../algorithm/generic/lib/ListPopulation.java | 225 ------------------- .../algorithm/generic/lib/MutationPolicy.java | 36 ---- .../cuboid/algorithm/generic/lib/Population.java | 61 ------ .../algorithm/generic/lib/SelectionPolicy.java | 35 --- .../algorithm/generic/lib/StoppingCondition.java | 36 ---- .../algorithm/generic/lib/TournamentSelection.java | 116 ---------- .../cuboid/algorithm/greedy/GreedyAlgorithm.java | 61 +++--- .../cube/cuboid/algorithm/ITAlgorithmTestBase.java | 9 - .../cuboid/algorithm/ITGeneticAlgorithmTest.java | 43 +++- 31 files changed, 447 insertions(+), 1609 deletions(-) diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/AbstractRecommendAlgorithm.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/AbstractRecommendAlgorithm.java index b35c738..094b960 100755 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/AbstractRecommendAlgorithm.java +++ b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/AbstractRecommendAlgorithm.java @@ -18,16 +18,17 @@ package org.apache.kylin.cube.cuboid.algorithm; -import java.util.concurrent.atomic.AtomicBoolean; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + public abstract class AbstractRecommendAlgorithm implements CuboidRecommendAlgorithm { private static final Logger logger = LoggerFactory.getLogger(AbstractRecommendAlgorithm.class); - private CuboidStats cuboidStats; - private BenefitPolicy benefitPolicy; + protected final CuboidStats cuboidStats; + protected final BenefitPolicy benefitPolicy; private AtomicBoolean cancelRequested = new AtomicBoolean(false); private AtomicBoolean canceled = new AtomicBoolean(false); @@ -45,13 +46,18 @@ public abstract class AbstractRecommendAlgorithm implements CuboidRecommendAlgor } @Override + public List<Long> recommend(double expansionRate) { + double spaceLimit = cuboidStats.getBaseCuboidSize() * expansionRate; + return start(spaceLimit); + } + + @Override public void cancel() { cancelRequested.set(true); } /** * Checks whether the algorithm has been canceled or timed out. - * */ protected boolean shouldCancel() { if (canceled.get()) { @@ -71,12 +77,4 @@ public abstract class AbstractRecommendAlgorithm implements CuboidRecommendAlgor } return false; } - - public CuboidStats getCuboidStats() { - return cuboidStats; - } - - public BenefitPolicy getBenefitPolicy() { - return benefitPolicy; - } } diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/BPUSCalculator.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/BPUSCalculator.java index 6d0b654..e293325 100755 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/BPUSCalculator.java +++ b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/BPUSCalculator.java @@ -18,15 +18,14 @@ package org.apache.kylin.cube.cuboid.algorithm; -import java.util.List; -import java.util.Map; -import java.util.Set; - +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import java.util.Map; +import java.util.Set; /** * Calculate the benefit based on Benefit Per Unit Space. @@ -35,17 +34,24 @@ public class BPUSCalculator implements BenefitPolicy { private static Logger logger = LoggerFactory.getLogger(BPUSCalculator.class); - protected CuboidStats cuboidStats; - protected Map<Long, Long> cuboidAggCostMap; + protected final CuboidStats cuboidStats; + protected final ImmutableMap<Long, Long> initCuboidAggCostMap; + protected final Map<Long, Long> processCuboidAggCostMap; public BPUSCalculator(CuboidStats cuboidStats) { this.cuboidStats = cuboidStats; - this.cuboidAggCostMap = Maps.newHashMap(); + this.initCuboidAggCostMap = ImmutableMap.copyOf(initCuboidAggCostMap()); + this.processCuboidAggCostMap = Maps.newHashMap(initCuboidAggCostMap); } - @Override - public void initBeforeStart() { - cuboidAggCostMap.clear(); + protected BPUSCalculator(CuboidStats cuboidStats, ImmutableMap<Long, Long> initCuboidAggCostMap) { + this.cuboidStats = cuboidStats; + this.initCuboidAggCostMap = initCuboidAggCostMap; + this.processCuboidAggCostMap = Maps.newHashMap(initCuboidAggCostMap); + } + + private Map<Long, Long> initCuboidAggCostMap() { + Map<Long, Long> cuboidAggCostMap = Maps.newHashMap(); //Initialize stats for mandatory cuboids for (Long cuboid : cuboidStats.getAllCuboidsForMandatory()) { if (getCuboidCost(cuboid) != null) { @@ -66,6 +72,7 @@ public class BPUSCalculator implements BenefitPolicy { } cuboidAggCostMap.put(cuboid, leastCost); } + return cuboidAggCostMap; } @Override @@ -88,22 +95,21 @@ public class BPUSCalculator implements BenefitPolicy { } @Override - public CuboidBenefitModel.BenefitModel calculateBenefitTotal(List<Long> cuboidsToAdd, Set<Long> selected) { + public CuboidBenefitModel.BenefitModel calculateBenefitTotal(Set<Long> cuboidsToAdd, Set<Long> selected) { Set<Long> selectedInner = Sets.newHashSet(selected); - Map<Long, Long> cuboidAggCostMapSnapshot = Maps.newHashMap(cuboidAggCostMap); + Map<Long, Long> cuboidAggCostMapCopy = Maps.newHashMap(processCuboidAggCostMap); for (Long cuboid : cuboidsToAdd) { selectedInner.add(cuboid); - propagateAggregationCost(cuboid, selectedInner); + propagateAggregationCost(cuboid, selectedInner, cuboidAggCostMapCopy); } double totalCostSaving = 0; int benefitCount = 0; - for (Long cuboid : cuboidAggCostMap.keySet()) { - if (cuboidAggCostMap.get(cuboid) < cuboidAggCostMapSnapshot.get(cuboid)) { - totalCostSaving += cuboidAggCostMapSnapshot.get(cuboid) - cuboidAggCostMap.get(cuboid); + for (Long cuboid : cuboidAggCostMapCopy.keySet()) { + if (cuboidAggCostMapCopy.get(cuboid) < processCuboidAggCostMap.get(cuboid)) { + totalCostSaving += processCuboidAggCostMap.get(cuboid) - cuboidAggCostMapCopy.get(cuboid); benefitCount++; } } - cuboidAggCostMap = cuboidAggCostMapSnapshot; double benefitPerUnitSpace = totalCostSaving; return new CuboidBenefitModel.BenefitModel(benefitPerUnitSpace, benefitCount); @@ -120,7 +126,7 @@ public class BPUSCalculator implements BenefitPolicy { } private long getCuboidAggregationCost(long cuboid) { - return cuboidAggCostMap.get(cuboid); + return processCuboidAggCostMap.get(cuboid); } @Override @@ -139,11 +145,15 @@ public class BPUSCalculator implements BenefitPolicy { @Override public void propagateAggregationCost(long cuboid, Set<Long> selected) { + propagateAggregationCost(cuboid, selected, processCuboidAggCostMap); + } + + public void propagateAggregationCost(long cuboid, Set<Long> selected, Map<Long, Long> processCuboidAggCostMap) { long aggregationCost = getCuboidCost(cuboid); Set<Long> childrenCuboids = cuboidStats.getAllDescendants(cuboid); for (Long child : childrenCuboids) { if (!selected.contains(child) && (aggregationCost < getCuboidAggregationCost(child))) { - cuboidAggCostMap.put(child, aggregationCost); + processCuboidAggCostMap.put(child, aggregationCost); } } } @@ -158,8 +168,6 @@ public class BPUSCalculator implements BenefitPolicy { @Override public BenefitPolicy getInstance() { - BPUSCalculator bpusCalculator = new BPUSCalculator(this.cuboidStats); - bpusCalculator.cuboidAggCostMap.putAll(this.cuboidAggCostMap); - return bpusCalculator; + return new BPUSCalculator(this.cuboidStats, this.initCuboidAggCostMap); } } \ No newline at end of file diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/BenefitPolicy.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/BenefitPolicy.java index 43bd3af..a10e51f 100755 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/BenefitPolicy.java +++ b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/BenefitPolicy.java @@ -18,7 +18,6 @@ package org.apache.kylin.cube.cuboid.algorithm; -import java.util.List; import java.util.Set; /** @@ -27,15 +26,29 @@ import java.util.Set; */ public interface BenefitPolicy { + /** + * @return a cloned instance with initial status + */ public BenefitPolicy getInstance(); - public void initBeforeStart(); - + /** + * @param selected should not be changed + * Will not change the inner instance status + */ public CuboidBenefitModel.BenefitModel calculateBenefit(long cuboid, Set<Long> selected); - public CuboidBenefitModel.BenefitModel calculateBenefitTotal(List<Long> cuboidsToAdd, Set<Long> selected); + /** + * @param cuboidsToAdd should not be changed + * @param selected should not be changed + * Will not change the inner instance status + */ + public CuboidBenefitModel.BenefitModel calculateBenefitTotal(Set<Long> cuboidsToAdd, Set<Long> selected); public boolean ifEfficient(CuboidBenefitModel best); + /** + * @param selected should not be changed + * Will update the inner instance status + */ public void propagateAggregationCost(long cuboid, Set<Long> selected); } diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/CuboidBenefitModel.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/CuboidBenefitModel.java index 42f1ecf..7e1edf5 100755 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/CuboidBenefitModel.java +++ b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/CuboidBenefitModel.java @@ -33,11 +33,11 @@ public class CuboidBenefitModel { } public Long getCuboidId() { - return cuboidModel == null ? null : cuboidModel.getCuboidId(); + return cuboidModel == null ? null : cuboidModel.cuboidId; } public Double getBenefit() { - return benefitModel == null ? null : benefitModel.getBenefit(); + return benefitModel == null ? null : benefitModel.benefit; } @Override @@ -45,45 +45,14 @@ public class CuboidBenefitModel { return "CuboidBenefitModel [cuboidModel=" + cuboidModel + ", benefitModel=" + benefitModel + "]"; } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((cuboidModel == null) ? 0 : cuboidModel.hashCode()); - result = prime * result + ((benefitModel == null) ? 0 : benefitModel.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - CuboidBenefitModel other = (CuboidBenefitModel) obj; - if (cuboidModel == null) { - if (other.cuboidModel != null) - return false; - } else if (!cuboidModel.equals(other.cuboidModel)) - return false; - if (benefitModel == null) { - if (other.benefitModel != null) - return false; - } else if (!benefitModel.equals(other.benefitModel)) - return false; - return true; - } - public static class CuboidModel { - private long cuboidId; + public final long cuboidId; - private long recordCount; - private double spaceSize; + public final long recordCount; + public final double spaceSize; - private double hitProbability; - private long scanCount; + public final double hitProbability; + public final long scanCount; public CuboidModel(long cuboId, long recordCount, double spaceSize, double hitProbability, long scanCount) { this.cuboidId = cuboId; @@ -93,118 +62,25 @@ public class CuboidBenefitModel { this.scanCount = scanCount; } - public long getCuboidId() { - return cuboidId; - } - - public long getRecordCount() { - return recordCount; - } - - public double getSpaceSize() { - return spaceSize; - } - - public double getHitProbability() { - return hitProbability; - } - - public long getScanCount() { - return scanCount; - } - @Override public String toString() { return "CuboidModel [cuboidId=" + cuboidId + ", recordCount=" + recordCount + ", spaceSize=" + spaceSize + ", hitProbability=" + hitProbability + ", scanCount=" + scanCount + "]"; } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (int) (cuboidId ^ (cuboidId >>> 32)); - result = prime * result + (int) (recordCount ^ (recordCount >>> 32)); - long temp; - temp = Double.doubleToLongBits(spaceSize); - result = prime * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(hitProbability); - result = prime * result + (int) (temp ^ (temp >>> 32)); - result = prime * result + (int) (scanCount ^ (scanCount >>> 32)); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - - CuboidModel other = (CuboidModel) obj; - if (cuboidId != other.cuboidId) - return false; - if (recordCount != other.recordCount) - return false; - if (Double.doubleToLongBits(spaceSize) != Double.doubleToLongBits(other.spaceSize)) - return false; - if (hitProbability != other.hitProbability) - return false; - if (scanCount != other.scanCount) - return false; - return true; - } } public static class BenefitModel { - private double benefit; - private int benefitCount; + public final double benefit; + public final int benefitCount; public BenefitModel(double benefit, int benefitCount) { this.benefit = benefit; this.benefitCount = benefitCount; } - public double getBenefit() { - return benefit; - } - - public int getBenefitCount() { - return benefitCount; - } - @Override public String toString() { return "BenefitModel [benefit=" + benefit + ", benefitCount=" + benefitCount + "]"; } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - long temp; - temp = Double.doubleToLongBits(benefit); - result = prime * result + (int) (temp ^ (temp >>> 32)); - result = prime * result + benefitCount; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - BenefitModel other = (BenefitModel) obj; - if (Double.doubleToLongBits(benefit) != Double.doubleToLongBits(other.benefit)) - return false; - if (benefitCount != other.benefitCount) - return false; - return true; - } } -} +} \ No newline at end of file diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/CuboidStats.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/CuboidStats.java index a1c191e..78a6c5b 100755 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/CuboidStats.java +++ b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/CuboidStats.java @@ -18,18 +18,17 @@ package org.apache.kylin.cube.cuboid.algorithm; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Map; +import java.util.Set; public class CuboidStats { private static final Logger logger = LoggerFactory.getLogger(CuboidStats.class); @@ -67,7 +66,7 @@ public class CuboidStats { } public Builder setRollingUpCountSourceMap(Map<Long, Map<Long, Long>> rollingUpCountSourceMap, - long rollUpThresholdForMandatory) { + long rollUpThresholdForMandatory) { this.rollingUpCountSourceMap = rollingUpCountSourceMap; this.rollUpThresholdForMandatory = rollUpThresholdForMandatory; return this; @@ -125,7 +124,7 @@ public class CuboidStats { private Map<Long, Set<Long>> allDescendantsCache; private CuboidStats(String key, long baseCuboidId, Set<Long> mandatoryCuboids, Map<Long, Long> statistics, - Map<Long, Double> size, Map<Long, Long> hitFrequencyMap, Map<Long, Map<Long, Long>> scanCountSourceMap) { + Map<Long, Double> size, Map<Long, Long> hitFrequencyMap, Map<Long, Map<Long, Long>> scanCountSourceMap) { this.key = key; this.baseCuboid = baseCuboidId; @@ -142,8 +141,8 @@ public class CuboidStats { cuboidsForSelection.removeAll(cuboidsForMandatory); //There's no overlap between mandatoryCuboidSet and selectionCuboidSet - this.mandatoryCuboidSet = ImmutableSet.<Long> builder().addAll(cuboidsForMandatory).build(); - this.selectionCuboidSet = ImmutableSet.<Long> builder().addAll(cuboidsForSelection).build(); + this.mandatoryCuboidSet = ImmutableSet.<Long>builder().addAll(cuboidsForMandatory).build(); + this.selectionCuboidSet = ImmutableSet.<Long>builder().addAll(cuboidsForSelection).build(); if (selectionCuboidSet.isEmpty()) { logger.warn("The selection set should not be empty!!!"); } @@ -151,8 +150,8 @@ public class CuboidStats { /** Initialize row count for mandatory cuboids */ CuboidStatsUtil.complementRowCountForMandatoryCuboids(statistics, baseCuboid, mandatoryCuboidSet); - this.cuboidCountMap = ImmutableMap.<Long, Long> builder().putAll(statistics).build(); - this.cuboidSizeMap = ImmutableMap.<Long, Double> builder().putAll(size).build(); + this.cuboidCountMap = ImmutableMap.<Long, Long>builder().putAll(statistics).build(); + this.cuboidSizeMap = ImmutableMap.<Long, Double>builder().putAll(size).build(); /** Initialize the hit probability for each selection cuboid */ Map<Long, Double> tmpCuboidHitProbabilityMap = Maps.newHashMapWithExpectedSize(selectionCuboidSet.size()); @@ -179,7 +178,7 @@ public class CuboidStats { tmpCuboidHitProbabilityMap.put(cuboid, 1.0 / selectionCuboidSet.size()); } } - this.cuboidHitProbabilityMap = ImmutableMap.<Long, Double> builder().putAll(tmpCuboidHitProbabilityMap).build(); + this.cuboidHitProbabilityMap = ImmutableMap.<Long, Double>builder().putAll(tmpCuboidHitProbabilityMap).build(); /** Initialize the scan count when query for each selection cuboid + one base cuboid */ Map<Long, Long> tmpCuboidScanCountMap = Maps.newHashMapWithExpectedSize(1 + selectionCuboidSet.size()); @@ -187,16 +186,16 @@ public class CuboidStats { for (Long cuboid : selectionCuboidSet) { tmpCuboidScanCountMap.put(cuboid, getExpScanCount(cuboid, statistics, scanCountSourceMap)); } - this.cuboidScanCountMap = ImmutableMap.<Long, Long> builder().putAll(tmpCuboidScanCountMap).build(); + this.cuboidScanCountMap = ImmutableMap.<Long, Long>builder().putAll(tmpCuboidScanCountMap).build(); - this.directChildrenCache = ImmutableMap.<Long, List<Long>> builder() + this.directChildrenCache = ImmutableMap.<Long, List<Long>>builder() .putAll(CuboidStatsUtil.createDirectChildrenCache(statistics.keySet())).build(); this.allDescendantsCache = Maps.newConcurrentMap(); } private long getExpScanCount(long sourceCuboid, Map<Long, Long> statistics, - Map<Long, Map<Long, Long>> scanCountSourceMap) { + Map<Long, Map<Long, Long>> scanCountSourceMap) { Preconditions.checkNotNull(statistics.get(sourceCuboid), "The statistics for source cuboid " + sourceCuboid + " does not exist!!!"); if (scanCountSourceMap == null || scanCountSourceMap.get(sourceCuboid) == null @@ -240,11 +239,11 @@ public class CuboidStats { } } - public Set<Long> getAllCuboidsForSelection() { + public ImmutableSet<Long> getAllCuboidsForSelection() { return selectionCuboidSet; } - public Set<Long> getAllCuboidsForMandatory() { + public ImmutableSet<Long> getAllCuboidsForMandatory() { return mandatoryCuboidSet; } diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/PBPUSCalculator.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/PBPUSCalculator.java index 6d3ddc7..9fddaec 100755 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/PBPUSCalculator.java +++ b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/PBPUSCalculator.java @@ -19,6 +19,7 @@ package org.apache.kylin.cube.cuboid.algorithm; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; /** * Calculate the benefit based on Benefit Per Unit Space with query probability distribution. @@ -29,6 +30,10 @@ public class PBPUSCalculator extends BPUSCalculator { super(cuboidStats); } + protected PBPUSCalculator(CuboidStats cuboidStats, ImmutableMap<Long, Long> initCuboidAggCostMap) { + super(cuboidStats, initCuboidAggCostMap); + } + @Override protected double getCostSaving(long descendant, long cuboid) { return getCuboidHitProbability(descendant) * super.getCostSaving(descendant, cuboid); @@ -39,15 +44,14 @@ public class PBPUSCalculator extends BPUSCalculator { } public double getMinBenefitRatio() { - Preconditions.checkArgument(cuboidStats.getAllCuboidsForSelection().size() > 0, + int cuboidDomainSize = cuboidStats.getAllCuboidsForSelection().size(); + Preconditions.checkArgument(cuboidDomainSize > 0, "The set of cuboids for selection is empty!!!"); - return super.getMinBenefitRatio() / cuboidStats.getAllCuboidsForSelection().size(); + return super.getMinBenefitRatio() / cuboidDomainSize; } @Override public BenefitPolicy getInstance() { - PBPUSCalculator pbpusCalculator = new PBPUSCalculator(cuboidStats); - pbpusCalculator.cuboidAggCostMap.putAll(this.cuboidAggCostMap); - return pbpusCalculator; + return new PBPUSCalculator(this.cuboidStats, this.initCuboidAggCostMap); } } diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/SPBPUSCalculator.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/SPBPUSCalculator.java index c89bf49..e235de3 100755 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/SPBPUSCalculator.java +++ b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/SPBPUSCalculator.java @@ -18,6 +18,8 @@ package org.apache.kylin.cube.cuboid.algorithm; +import com.google.common.collect.ImmutableMap; + /** * Calculate the benefit based on Benefit Per Unit Space with query probability distribution and updated cost. */ @@ -27,6 +29,10 @@ public class SPBPUSCalculator extends PBPUSCalculator { super(cuboidStats); } + protected SPBPUSCalculator(CuboidStats cuboidStats, ImmutableMap<Long, Long> initCuboidAggCostMap) { + super(cuboidStats, initCuboidAggCostMap); + } + @Override protected Long getCuboidCost(long cuboid) { return cuboidStats.getCuboidQueryCost(cuboid); @@ -34,8 +40,6 @@ public class SPBPUSCalculator extends PBPUSCalculator { @Override public BenefitPolicy getInstance() { - SPBPUSCalculator spbpusCalculator = new SPBPUSCalculator(cuboidStats); - spbpusCalculator.cuboidAggCostMap.putAll(this.cuboidAggCostMap); - return spbpusCalculator; + return new SPBPUSCalculator(this.cuboidStats, this.initCuboidAggCostMap); } } diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/BitsChromosome.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/BitsChromosome.java index 6445ca6..81d7e20 100755 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/BitsChromosome.java +++ b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/BitsChromosome.java @@ -18,70 +18,72 @@ package org.apache.kylin.cube.cuboid.algorithm.generic; -import java.util.BitSet; -import java.util.List; -import java.util.Set; - +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import org.apache.commons.math3.genetics.Chromosome; import org.apache.kylin.cube.cuboid.algorithm.BenefitPolicy; import org.apache.kylin.cube.cuboid.algorithm.CuboidBenefitModel; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.Chromosome; -import org.apache.kylin.cube.cuboid.algorithm.CuboidStats; -import com.google.common.collect.Sets; +import java.util.BitSet; public class BitsChromosome extends Chromosome { - private BitSet key; - private int length; - private BenefitPolicy benefitPolicy; - private CuboidStats cuboidStats; - private CuboidEncoder cuboidEncoder; - private double spaceLimit; - private double spaceCost; - private double fitness = -1; - - public BitsChromosome(final BitSet key, final BenefitPolicy benefitPolicy, final CuboidStats cuboidStats, - final double spaceLimit) { - super(); - this.key = key; - this.length = cuboidStats.getAllCuboidsForSelection().size(); - this.benefitPolicy = benefitPolicy.getInstance(); - this.cuboidStats = cuboidStats; - this.cuboidEncoder = new CuboidEncoder(cuboidStats); - this.spaceLimit = spaceLimit; - initSpaceCost(); - } + /** + * Global unmodified + */ + private final BitsChromosomeHelper helper; + + /** + * BitSet representing the chromosome + */ + private final BitSet representation; + private final ImmutableSet<Long> cuboids; + + private final BenefitPolicy benefitPolicy; - public BitsChromosome newBitsChromosome(BitSet newkey) { - return new BitsChromosome(newkey, this.benefitPolicy, this.cuboidStats, this.spaceLimit); + private final double spaceCost; + + public BitsChromosome(final BitSet representation, final BenefitPolicy benefitPolicy, BitsChromosomeHelper helper) { + this.helper = helper; + + this.representation = representation; + this.cuboids = ImmutableSet.copyOf(helper.toCuboidList(representation)); + + this.benefitPolicy = benefitPolicy; + + this.spaceCost = helper.getCuboidSize(Sets.newHashSet(cuboids)); } - private void initSpaceCost() { - spaceCost = 0; - List<Long> remainingCuboids = cuboidEncoder.toCuboidList(key); - for (Long cuboid : remainingCuboids) { - spaceCost += cuboidStats.getCuboidSize(cuboid); - } + public BitsChromosome newBitsChromosome(BitSet newRepresentation) { + return new BitsChromosome(newRepresentation, benefitPolicy.getInstance(), helper); } - public BitSet getKey() { - return key; + public BitSet getRepresentation() { + return representation; } + /** + * Returns the length of the chromosome. + * + * @return the length of the chromosome + */ public int getLength() { - return length; + return helper.getLength(); } - public CuboidEncoder getCuboidEncoder() { - return cuboidEncoder; + public ImmutableSet<Long> getCuboids() { + return cuboids; } @Override - public double fitness() { - if (fitness == -1) { - fitness = calculateFitness(); + public synchronized double fitness() { + CuboidBenefitModel.BenefitModel benefitModel = benefitPolicy.calculateBenefitTotal(cuboids, + helper.getMandatoryCuboids()); + double totalBenefit = benefitModel.benefit; + if (spaceCost > helper.spaceLimit) { + totalBenefit = totalBenefit * helper.spaceLimit / spaceCost; } - return fitness; + return totalBenefit; } @Override @@ -89,45 +91,25 @@ public class BitsChromosome extends Chromosome { return this.equals(another); } - private synchronized double calculateFitness() { - List<Long> remainingCuboids = cuboidEncoder.toCuboidList(key); - Set<Long> selectedCuboidSets = Sets.newHashSet(); - selectedCuboidSets.addAll(cuboidStats.getAllCuboidsForMandatory()); + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + BitsChromosome that = (BitsChromosome) o; + + if (helper != null ? !helper.equals(that.helper) : that.helper != null) + return false; + return representation != null ? representation.equals(that.representation) : that.representation == null; - CuboidBenefitModel.BenefitModel benefitModel = benefitPolicy.calculateBenefitTotal(remainingCuboids, selectedCuboidSets); - double totalBenefit = benefitModel.getBenefit(); - if (spaceCost > spaceLimit) { - totalBenefit = totalBenefit * spaceLimit / spaceCost; - } - return totalBenefit; } @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + length; + int result = helper != null ? helper.hashCode() : 0; + result = 31 * result + (representation != null ? representation.hashCode() : 0); return result; } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - BitsChromosome other = (BitsChromosome) obj; - if (length != other.length) { - return false; - } - if (key == null) { - if (other.key != null) - return false; - } else if (!key.equals(other.key)) - return false; - return true; - } } diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/BitsChromosomeHelper.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/BitsChromosomeHelper.java new file mode 100644 index 0000000..2b5d143 --- /dev/null +++ b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/BitsChromosomeHelper.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.apache.kylin.cube.cuboid.algorithm.generic; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import org.apache.kylin.cube.cuboid.algorithm.CuboidStats; + +import java.util.BitSet; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class BitsChromosomeHelper { + + public final double spaceLimit; + private final CuboidStats cuboidStats; + private final CuboidEncoder cuboidEncoder; + + public BitsChromosomeHelper(final double spaceLimit, final CuboidStats cuboidStats) { + this.spaceLimit = spaceLimit; + this.cuboidStats = cuboidStats; + this.cuboidEncoder = new CuboidEncoder(cuboidStats.getAllCuboidsForSelection()); + } + + public ImmutableSet<Long> getMandatoryCuboids() { + return cuboidStats.getAllCuboidsForMandatory(); + } + + public List<Long> toCuboidList(BitSet bits) { + return cuboidEncoder.toCuboidList(bits); + } + + public double getCuboidSize(Set<Long> cuboids) { + double ret = 0; + for (Long cuboid : cuboids) { + ret += cuboidStats.getCuboidSize(cuboid); + } + return ret; + } + + public double getCuboidSizeByBitIndex(int index) { + return cuboidStats.getCuboidSize(cuboidEncoder.cuboidDomain.get(index)); + } + + public int getLength() { + return cuboidEncoder.cuboidDomain.size(); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + BitsChromosomeHelper that = (BitsChromosomeHelper) o; + + return cuboidEncoder != null ? cuboidEncoder.equals(that.cuboidEncoder) : that.cuboidEncoder == null; + + } + + @Override + public int hashCode() { + return cuboidEncoder != null ? cuboidEncoder.hashCode() : 0; + } + + private static class CuboidEncoder { + public final ImmutableList<Long> cuboidDomain; + + public CuboidEncoder(Set<Long> cuboidSet) { + List<Long> cuboidList = Lists.newArrayList(cuboidSet); + Collections.sort(cuboidList, Collections.reverseOrder()); + this.cuboidDomain = ImmutableList.copyOf(cuboidList); + } + + public List<Long> toCuboidList(BitSet bits) { + List<Long> cuboids = Lists.newArrayListWithExpectedSize(bits.cardinality()); + for (int i = bits.nextSetBit(0); i >= 0; i = bits.nextSetBit(i + 1)) { + cuboids.add(cuboidDomain.get(i)); + } + return cuboids; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + CuboidEncoder that = (CuboidEncoder) o; + + return cuboidDomain != null ? cuboidDomain.equals(that.cuboidDomain) : that.cuboidDomain == null; + + } + + @Override + public int hashCode() { + return cuboidDomain != null ? cuboidDomain.hashCode() : 0; + } + } +} diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/BitsMutation.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/BitsMutation.java similarity index 70% rename from core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/BitsMutation.java rename to core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/BitsMutation.java index 073b947..2cd4b99 100755 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/BitsMutation.java +++ b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/BitsMutation.java @@ -16,18 +16,20 @@ * limitations under the License. */ -package org.apache.kylin.cube.cuboid.algorithm.generic.lib; +package org.apache.kylin.cube.cuboid.algorithm.generic; -import java.util.BitSet; +import org.apache.commons.math3.exception.MathIllegalArgumentException; +import org.apache.commons.math3.exception.util.DummyLocalizable; +import org.apache.commons.math3.genetics.Chromosome; +import org.apache.commons.math3.genetics.GeneticAlgorithm; +import org.apache.commons.math3.genetics.MutationPolicy; -import org.apache.kylin.cube.cuboid.algorithm.generic.BitsChromosome; -import org.apache.kylin.cube.cuboid.algorithm.generic.GeneticAlgorithm; +import java.util.BitSet; /** * Modified from the BinaryMutation.java in https://github.com/apache/commons-math - * + * <p> * Mutation for {@link BitsChromosome}s. Randomly changes one gene. - * */ public class BitsMutation implements MutationPolicy { @@ -40,22 +42,18 @@ public class BitsMutation implements MutationPolicy { */ public Chromosome mutate(Chromosome original) throws IllegalArgumentException { if (!(original instanceof BitsChromosome)) { - throw new IllegalArgumentException("Chromosome " + original.getClass() + " must be of type BitsChromosome."); + throw new MathIllegalArgumentException(new DummyLocalizable("bits mutation only works on BitsChromosome")); } BitsChromosome origChrom = (BitsChromosome) original; - BitSet newNey = (BitSet) origChrom.getKey().clone(); + BitSet newNey = (BitSet) origChrom.getRepresentation().clone(); // randomly select a gene - int geneIndex = getMutationGeneIndex(origChrom); + int geneIndex = GeneticAlgorithm.getRandomGenerator().nextInt(origChrom.getLength()); // change it - boolean value = newNey.get(geneIndex); - newNey.set(geneIndex, !value); + newNey.set(geneIndex, !newNey.get(geneIndex)); + Chromosome newChrom = origChrom.newBitsChromosome(newNey); return newChrom; } - - protected int getMutationGeneIndex(BitsChromosome origChrom) { - return GeneticAlgorithm.RANDGEN.get().nextInt(origChrom.getLength()); - } } diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/OnePointCrossover.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/BitsOnePointCrossover.java old mode 100755 new mode 100644 similarity index 72% rename from core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/OnePointCrossover.java rename to core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/BitsOnePointCrossover.java index 5ef8cfa..00559d3 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/OnePointCrossover.java +++ b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/BitsOnePointCrossover.java @@ -16,20 +16,25 @@ * limitations under the License. */ -package org.apache.kylin.cube.cuboid.algorithm.generic.lib; +package org.apache.kylin.cube.cuboid.algorithm.generic; -import java.util.BitSet; +import org.apache.commons.math3.exception.DimensionMismatchException; +import org.apache.commons.math3.exception.MathIllegalArgumentException; +import org.apache.commons.math3.exception.util.DummyLocalizable; +import org.apache.commons.math3.genetics.Chromosome; +import org.apache.commons.math3.genetics.ChromosomePair; +import org.apache.commons.math3.genetics.CrossoverPolicy; +import org.apache.commons.math3.genetics.GeneticAlgorithm; -import org.apache.kylin.cube.cuboid.algorithm.generic.BitsChromosome; -import org.apache.kylin.cube.cuboid.algorithm.generic.GeneticAlgorithm; +import java.util.BitSet; /** * Modified from the OnePointCrossover.java in https://github.com/apache/commons-math - * + * <p> * One point crossover policy. A random crossover point is selected and the * first part from each parent is copied to the corresponding child, and the * second parts are copied crosswise. - * + * <p> * Example: * <pre> * -C- denotes a crossover point @@ -41,19 +46,17 @@ import org.apache.kylin.cube.cuboid.algorithm.generic.GeneticAlgorithm; * /------------\ /-----\ /------------\ /-----\ * c1 = (1 0 1 0 0 1 | 1 1 1) X c2 = (0 1 1 0 1 0 | 0 1 1) * </pre> - * + * <p> * This policy works only on {@link BitsChromosome}, and therefore it * is parameterized by T. Moreover, the chromosomes must have same lengths. - * - * */ -public class OnePointCrossover implements CrossoverPolicy { +public class BitsOnePointCrossover implements CrossoverPolicy { /** * Performs one point crossover. A random crossover point is selected and the * first part from each parent is copied to the corresponding child, and the * second parts are copied crosswise. - * + * <p> * Example: * <pre> * -C- denotes a crossover point @@ -66,20 +69,20 @@ public class OnePointCrossover implements CrossoverPolicy { * c1 = (1 0 1 0 0 1 | 1 1 1) X c2 = (0 1 1 0 1 0 | 0 1 1) * </pre> * - * @param first first parent (p1) + * @param first first parent (p1) * @param second second parent (p2) * @return pair of two children (c1,c2) - * @throws IllegalArgumentException if one of the chromosomes is - * not an instance of {@link BitsChromosome} - * @throws ChromosomeMismatchException if the length of the two chromosomes is different + * @throws IllegalArgumentException if one of the chromosomes is + * not an instance of {@link BitsChromosome} + * @throws MathIllegalArgumentException if the length of the two chromosomes is different */ @SuppressWarnings("unchecked") // OK because of instanceof checks public ChromosomePair crossover(final Chromosome first, final Chromosome second) - throws IllegalArgumentException, ChromosomeMismatchException { + throws DimensionMismatchException, MathIllegalArgumentException { if (!(first instanceof BitsChromosome && second instanceof BitsChromosome)) { - throw new IllegalArgumentException("Chromosome first " + first.getClass() + " and second " - + second.getClass() + " must be of type BitsChromosome."); + throw new MathIllegalArgumentException( + new DummyLocalizable("bits one-point crossover only works on BitsChromosome")); } return crossover((BitsChromosome) first, (BitsChromosome) second); } @@ -87,26 +90,26 @@ public class OnePointCrossover implements CrossoverPolicy { /** * Helper for {@link #crossover(Chromosome, Chromosome)}. Performs the actual crossover. * - * @param first the first chromosome. + * @param first the first chromosome. * @param second the second chromosome. * @return the pair of new chromosomes that resulted from the crossover. - * @throws ChromosomeMismatchException if the length of the two chromosomes is different + * @throws DimensionMismatchException if the length of the two chromosomes is different */ private ChromosomePair crossover(final BitsChromosome first, final BitsChromosome second) - throws ChromosomeMismatchException { + throws DimensionMismatchException { final int length = first.getLength(); if (length != second.getLength()) { - throw new ChromosomeMismatchException("BitsChromosome length mismatch.", second.getLength(), length); + throw new DimensionMismatchException(second.getLength(), length); } - final BitSet parent1Key = first.getKey(); - final BitSet parent2Key = second.getKey(); + final BitSet parent1Key = first.getRepresentation(); + final BitSet parent2Key = second.getRepresentation(); final BitSet child1Key = new BitSet(length); final BitSet child2Key = new BitSet(length); // select a crossover point at random (0 and length makes no sense) - final int crossoverIndex = 1 + (GeneticAlgorithm.RANDGEN.get().nextInt(length - 2)); + final int crossoverIndex = 1 + (GeneticAlgorithm.getRandomGenerator().nextInt(length - 2)); BitSet a = (BitSet) parent1Key.clone(); a.clear(crossoverIndex, length); @@ -125,4 +128,4 @@ public class OnePointCrossover implements CrossoverPolicy { child2Key.or(b); return new ChromosomePair(first.newBitsChromosome(child1Key), second.newBitsChromosome(child2Key)); } -} +} \ No newline at end of file diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/CombinedStoppingCondition.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/CombinedStoppingCondition.java index bc4bb99..a12c44d 100755 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/CombinedStoppingCondition.java +++ b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/CombinedStoppingCondition.java @@ -18,8 +18,8 @@ package org.apache.kylin.cube.cuboid.algorithm.generic; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.Population; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.StoppingCondition; +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.StoppingCondition; import java.util.List; diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/CuboidEncoder.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/CuboidEncoder.java deleted file mode 100755 index 7e48413..0000000 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/CuboidEncoder.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -package org.apache.kylin.cube.cuboid.algorithm.generic; - -import java.util.BitSet; -import java.util.Collections; -import java.util.List; - -import org.apache.kylin.cube.cuboid.algorithm.CuboidStats; - -import com.google.common.collect.Lists; - -public class CuboidEncoder { - private List<Long> selectionCuboids; - - public CuboidEncoder(CuboidStats cuboidStats) { - selectionCuboids = Lists.newArrayList(cuboidStats.getAllCuboidsForSelection()); - Collections.sort(selectionCuboids, Collections.reverseOrder()); - } - - public List<Long> toCuboidList(BitSet bits) { - List<Long> cuboids = Lists.newArrayListWithCapacity(bits.cardinality()); - for (int i = bits.nextSetBit(0); i >= 0; i = bits.nextSetBit(i + 1)) { - cuboids.add(selectionCuboids.get(i)); - } - return cuboids; - } -} diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/GeneticAlgorithm.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/GeneticAlgorithm.java index ce4dc3e..27d59fa 100755 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/GeneticAlgorithm.java +++ b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/GeneticAlgorithm.java @@ -18,91 +18,70 @@ package org.apache.kylin.cube.cuboid.algorithm.generic; -import java.util.BitSet; -import java.util.List; -import java.util.Random; - +import com.google.common.collect.Lists; +import org.apache.commons.math3.genetics.Chromosome; +import org.apache.commons.math3.genetics.ElitisticListPopulation; +import org.apache.commons.math3.genetics.FixedGenerationCount; +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.StoppingCondition; import org.apache.kylin.cube.cuboid.algorithm.AbstractRecommendAlgorithm; import org.apache.kylin.cube.cuboid.algorithm.BenefitPolicy; import org.apache.kylin.cube.cuboid.algorithm.CuboidStats; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.BitsMutation; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.Chromosome; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.ChromosomePair; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.CrossoverPolicy; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.ElitisticListPopulation; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.FixedGenerationCount; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.MutationPolicy; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.OnePointCrossover; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.Population; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.SelectionPolicy; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.StoppingCondition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.Lists; +import java.util.BitSet; +import java.util.List; /** - * Modified from the GeneticAlgorithm.java in https://github.com/apache/commons-math - * - * Implementation of a genetic algorithm to recommend a list of cuboids. All factors that govern the processing - * of the algorithm can be configured. - * + * Implementation of a genetic algorithm to recommend a list of cuboids. + * All factors that govern the processing of the algorithm can be configured. */ public class GeneticAlgorithm extends AbstractRecommendAlgorithm { private static final Logger logger = LoggerFactory.getLogger(GeneticAlgorithm.class); - public static ThreadLocal<Random> RANDGEN = new ThreadLocal<Random>() { - @Override - protected Random initialValue() { - return new Random(System.currentTimeMillis() * Thread.currentThread().getId()); - } - }; - /** the rate of crossover for the algorithm. */ + + private final org.apache.commons.math3.genetics.GeneticAlgorithm geneticAlgorithm; + + /** + * the rate of crossover for the algorithm. + */ private final double crossoverRate = 0.9; - /** the rate of mutation for the algorithm. */ + /** + * the rate of mutation for the algorithm. + */ private final double mutationRate = 0.001; - /** the init population size. */ + /** + * the init population size. + */ private final int populationSize = 500; - /** the max population size. */ + /** + * the max population size. + */ private final int maxPopulationSize = 510; - private CrossoverPolicy crossoverPolicy; - private MutationPolicy mutationPolicy; - private SelectionPolicy selectionPolicy; - private CuboidEncoder cuboidEncoder; - /** the number of generations evolved to reach {@link StoppingCondition} in the last run. */ - private int generationsEvolved = 0; public GeneticAlgorithm(final long timeout, BenefitPolicy benefitPolicy, CuboidStats cuboidStats) { super(timeout, benefitPolicy, cuboidStats); - this.crossoverPolicy = new OnePointCrossover(); - this.mutationPolicy = new BitsMutation(); - this.selectionPolicy = new RouletteWheelSelection(); - - this.cuboidEncoder = new CuboidEncoder(getCuboidStats()); - } - - @Override - public List<Long> recommend(double expansionRate) { - double spaceLimit = getCuboidStats().getBaseCuboidSize() * expansionRate; - return start(spaceLimit); + this.geneticAlgorithm = new org.apache.commons.math3.genetics.GeneticAlgorithm(new BitsOnePointCrossover(), + crossoverRate, new BitsMutation(), mutationRate, new RouletteWheelSelection()); } @Override public List<Long> start(double maxSpaceLimit) { logger.debug("Genetic Algorithm started."); - getBenefitPolicy().initBeforeStart(); - //Initial mandatory cuboids double remainingSpace = maxSpaceLimit; - for (Long mandatoryOne : getCuboidStats().getAllCuboidsForMandatory()) { - if (getCuboidStats().getCuboidSize(mandatoryOne) != null) { - remainingSpace -= getCuboidStats().getCuboidSize(mandatoryOne); + for (Long mandatoryOne : cuboidStats.getAllCuboidsForMandatory()) { + if (cuboidStats.getCuboidSize(mandatoryOne) != null) { + remainingSpace -= cuboidStats.getCuboidSize(mandatoryOne); } } + BitsChromosomeHelper helper = new BitsChromosomeHelper(remainingSpace, cuboidStats); + //Generate a population randomly - Population initial = initRandomPopulation(remainingSpace); + Population initial = initRandomPopulation(helper); //Set stopping condition List<StoppingCondition> conditions = Lists.newArrayList(); @@ -110,17 +89,17 @@ public class GeneticAlgorithm extends AbstractRecommendAlgorithm { CombinedStoppingCondition stopCondition = new CombinedStoppingCondition(conditions); //Start the evolution - Population current = evolve(initial, stopCondition); + Population current = geneticAlgorithm.evolve(initial, stopCondition); BitsChromosome chromosome = (BitsChromosome) current.getFittestChromosome(); logger.debug("Genetic Algorithm finished."); List<Long> finalList = Lists.newArrayList(); - finalList.addAll(getCuboidStats().getAllCuboidsForMandatory()); - finalList.addAll(cuboidEncoder.toCuboidList(chromosome.getKey())); + finalList.addAll(helper.getMandatoryCuboids()); + finalList.addAll(chromosome.getCuboids()); double totalSpace = 0; if (logger.isTraceEnabled()) { for (Long cuboid : finalList) { - Double unitSpace = getCuboidStats().getCuboidSize(cuboid); + Double unitSpace = cuboidStats.getCuboidSize(cuboid); if (unitSpace != null) { logger.trace(String.format("cuboidId %d and Space: %f", cuboid, unitSpace)); totalSpace += unitSpace; @@ -129,157 +108,31 @@ public class GeneticAlgorithm extends AbstractRecommendAlgorithm { } } logger.trace("Total Space:" + totalSpace); - logger.trace("Space Expansion Rate:" + totalSpace / getCuboidStats().getBaseCuboidSize()); + logger.trace("Space Expansion Rate:" + totalSpace / cuboidStats.getBaseCuboidSize()); } return finalList; } - protected Population initRandomPopulation(double maxSpaceLimit) { + protected Population initRandomPopulation(BitsChromosomeHelper helper) { List<Chromosome> chromosomeList = Lists.newArrayListWithCapacity(populationSize); - List<Long> cuboidsForSelection = Lists.newArrayList(getCuboidStats().getAllCuboidsForSelection()); - int selectionSize = cuboidsForSelection.size(); while (chromosomeList.size() < populationSize) { - BitSet bitSetForSelection = new BitSet(selectionSize); + BitSet bitSetForSelection = new BitSet(helper.getLength()); //Initialize selection genes double totalSpace = 0; - while (totalSpace < maxSpaceLimit) { - int j = RANDGEN.get().nextInt(selectionSize); + while (totalSpace < helper.spaceLimit) { + int j = org.apache.commons.math3.genetics.GeneticAlgorithm.getRandomGenerator() + .nextInt(helper.getLength()); if (!bitSetForSelection.get(j)) { - totalSpace += getCuboidStats().getCuboidSize(cuboidsForSelection.get(j)); + totalSpace += helper.getCuboidSizeByBitIndex(j); bitSetForSelection.set(j); } } - Chromosome chromosome = new BitsChromosome(bitSetForSelection, getBenefitPolicy(), getCuboidStats(), - maxSpaceLimit); + Chromosome chromosome = new BitsChromosome(bitSetForSelection, benefitPolicy.getInstance(), helper); chromosomeList.add(chromosome); } return new ElitisticListPopulation(chromosomeList, maxPopulationSize, 0.8); } - - /** - * Evolve the given population. Evolution stops when the stopping condition - * is satisfied. Updates the {@link #getGenerationsEvolved() generationsEvolved} - * property with the number of generations evolved before the StoppingCondition - * is satisfied. - * - * @param initial the initial, seed population. - * @param condition the stopping condition used to stop evolution. - * @return the population that satisfies the stopping condition. - */ - protected Population evolve(final Population initial, final StoppingCondition condition) { - Population current = initial; - generationsEvolved = 0; - while (!condition.isSatisfied(current) && (!shouldCancel())) { - current = nextGeneration(current); - generationsEvolved++; - logger.trace("Generations evolved count:" + generationsEvolved); - } - return current; - } - - /** - * Evolve the given population into the next generation. - * <p> - * <ol> - * <li>Get nextGeneration population to fill from <code>current</code> - * generation, using its nextGeneration method</li> - * <li>Loop until new generation is filled:</li> - * <ul><li>Apply configured SelectionPolicy to select a pair of parents - * from <code>current</code></li> - * <li>With probability = {@link #getCrossoverRate()}, apply - * configured {@link CrossoverPolicy} to parents</li> - * <li>With probability = {@link #getMutationRate()}, apply - * configured {@link MutationPolicy} to each of the offspring</li> - * <li>Add offspring individually to nextGeneration, - * space permitting</li> - * </ul> - * <li>Return nextGeneration</li> - * </ol> - * - * @param current the current population. - * @return the population for the next generation. - */ - protected Population nextGeneration(final Population current) { - Population nextGeneration = current.nextGeneration(); - - while (nextGeneration.getPopulationSize() < nextGeneration.getPopulationLimit()) { - // select parent chromosomes - ChromosomePair pair = getSelectionPolicy().select(current); - - // crossover? - if (RANDGEN.get().nextDouble() < getCrossoverRate()) { - // apply crossover policy to create two offspring - pair = getCrossoverPolicy().crossover(pair.getFirst(), pair.getSecond()); - } - - // mutation? - if (RANDGEN.get().nextDouble() < getMutationRate()) { - // apply mutation policy to the chromosomes - pair = new ChromosomePair(getMutationPolicy().mutate(pair.getFirst()), - getMutationPolicy().mutate(pair.getSecond())); - } - - // add the first chromosome to the population - nextGeneration.addChromosome(pair.getFirst()); - // is there still a place for the second chromosome? - if (nextGeneration.getPopulationSize() < nextGeneration.getPopulationLimit()) { - // add the second chromosome to the population - nextGeneration.addChromosome(pair.getSecond()); - } - } - return nextGeneration; - } - - /** - * Returns the crossover policy. - * @return crossover policy - */ - public CrossoverPolicy getCrossoverPolicy() { - return crossoverPolicy; - } - - /** - * Returns the crossover rate. - * @return crossover rate - */ - public double getCrossoverRate() { - return crossoverRate; - } - - /** - * Returns the mutation policy. - * @return mutation policy - */ - public MutationPolicy getMutationPolicy() { - return mutationPolicy; - } - - /** - * Returns the mutation rate. - * @return mutation rate - */ - public double getMutationRate() { - return mutationRate; - } - - /** - * Returns the selection policy. - * @return selection policy - */ - public SelectionPolicy getSelectionPolicy() { - return selectionPolicy; - } - - /** - * Returns the number of generations evolved to reach {@link StoppingCondition} in the last run. - * - * @return number of generations evolved - */ - public int getGenerationsEvolved() { - return generationsEvolved; - } - } diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/RouletteWheelSelection.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/RouletteWheelSelection.java index b93ed31..555e57e 100755 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/RouletteWheelSelection.java +++ b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/RouletteWheelSelection.java @@ -19,11 +19,12 @@ package org.apache.kylin.cube.cuboid.algorithm.generic; import com.google.common.collect.Lists; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.Chromosome; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.ChromosomePair; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.ListPopulation; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.Population; -import org.apache.kylin.cube.cuboid.algorithm.generic.lib.SelectionPolicy; +import org.apache.commons.math3.genetics.Chromosome; +import org.apache.commons.math3.genetics.ChromosomePair; +import org.apache.commons.math3.genetics.GeneticAlgorithm; +import org.apache.commons.math3.genetics.ListPopulation; +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.SelectionPolicy; import java.util.List; @@ -47,7 +48,7 @@ public class RouletteWheelSelection implements SelectionPolicy { } private Chromosome rouletteWheel(final List<Chromosome> chromosomes, final double totalFitness) { - double rnd = (GeneticAlgorithm.RANDGEN.get().nextDouble() * totalFitness); + double rnd = (GeneticAlgorithm.getRandomGenerator().nextDouble() * totalFitness); double runningScore = 0; for (Chromosome o : chromosomes) { if (rnd >= runningScore && rnd <= runningScore + o.getFitness()) { diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/Chromosome.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/Chromosome.java deleted file mode 100755 index a58abb1..0000000 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/Chromosome.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -package org.apache.kylin.cube.cuboid.algorithm.generic.lib; - -/** - * Modified from the Chromosome.java in https://github.com/apache/commons-math - * - * Individual in a population. Chromosomes are compared based on their fitness. - * <p> - * The chromosomes are IMMUTABLE, and so their fitness is also immutable and - * therefore it can be cached. - * - */ -public abstract class Chromosome implements Comparable<Chromosome>, Fitness { - /** Value assigned when no fitness has been computed yet. */ - private static final double NO_FITNESS = Double.NEGATIVE_INFINITY; - - /** Cached value of the fitness of this chromosome. */ - private double fitness = NO_FITNESS; - - /** - * Access the fitness of this chromosome. The bigger the fitness, the better the chromosome. - * <p> - * Computation of fitness is usually very time-consuming task, therefore the fitness is cached. - * - * @return the fitness - */ - public double getFitness() { - if (this.fitness == NO_FITNESS) { - // no cache - compute the fitness - this.fitness = fitness(); - } - return this.fitness; - } - - /** - * Compares two chromosomes based on their fitness. The bigger the fitness, the better the chromosome. - * - * @param another another chromosome to compare - * @return - * <ul> - * <li>-1 if <code>another</code> is better than <code>this</code></li> - * <li>1 if <code>another</code> is worse than <code>this</code></li> - * <li>0 if the two chromosomes have the same fitness</li> - * </ul> - */ - public int compareTo(final Chromosome another) { - return Double.compare(getFitness(), another.getFitness()); - } - - /** - * Returns <code>true</code> iff <code>another</code> has the same representation and therefore the same fitness. By - * default, it returns false -- override it in your implementation if you need it. - * - * @param another chromosome to compare - * @return true if <code>another</code> is equivalent to this chromosome - */ - protected boolean isSame(final Chromosome another) { - return false; - } - - /** - * Searches the <code>population</code> for another chromosome with the same representation. If such chromosome is - * found, it is returned, if no such chromosome exists, returns <code>null</code>. - * - * @param population Population to search - * @return Chromosome with the same representation, or <code>null</code> if no such chromosome exists. - */ - protected Chromosome findSameChromosome(final Population population) { - for (Chromosome anotherChr : population) { - if (this.isSame(anotherChr)) { - return anotherChr; - } - } - return null; - } - - /** - * Searches the population for a chromosome representing the same solution, and if it finds one, - * updates the fitness to its value. - * - * @param population Population to search - */ - public void searchForFitnessUpdate(final Population population) { - Chromosome sameChromosome = findSameChromosome(population); - if (sameChromosome != null) { - fitness = sameChromosome.getFitness(); - } - } -} diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/ChromosomeMismatchException.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/ChromosomeMismatchException.java deleted file mode 100755 index 51d5cb3..0000000 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/ChromosomeMismatchException.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -package org.apache.kylin.cube.cuboid.algorithm.generic.lib; - -/** - * Exception to be thrown when two Chromosomes length differ. - * - */ -public class ChromosomeMismatchException extends IllegalArgumentException { - private static final long serialVersionUID = -7483865132286153255L; - - private final int length; - - /** - * Construct an exception from the mismatched chromosomes. - * - * @param errorMsg error info. - * @param wrong Wrong length. - * @param expected Expected length. - */ - public ChromosomeMismatchException(String errorMsg, int wrong, int expected) { - super(errorMsg); - length = expected; - } - - /** - * @return the expected length. - */ - public int getLength() { - return length; - } -} diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/ChromosomePair.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/ChromosomePair.java deleted file mode 100755 index aa0f9df..0000000 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/ChromosomePair.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -package org.apache.kylin.cube.cuboid.algorithm.generic.lib; - -/** - * Modified from the ChromosomePair.java in https://github.com/apache/commons-math - * - * A pair of {@link Chromosome} objects. - * - */ -public class ChromosomePair { - /** the first chromosome in the pair. */ - private final Chromosome first; - - /** the second chromosome in the pair. */ - private final Chromosome second; - - /** - * Create a chromosome pair. - * - * @param c1 the first chromosome. - * @param c2 the second chromosome. - */ - public ChromosomePair(final Chromosome c1, final Chromosome c2) { - super(); - first = c1; - second = c2; - } - - /** - * Access the first chromosome. - * - * @return the first chromosome. - */ - public Chromosome getFirst() { - return first; - } - - /** - * Access the second chromosome. - * - * @return the second chromosome. - */ - public Chromosome getSecond() { - return second; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return String.format("(%s,%s)", getFirst(), getSecond()); - } -} diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/CrossoverPolicy.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/CrossoverPolicy.java deleted file mode 100755 index 397075c..0000000 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/CrossoverPolicy.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -package org.apache.kylin.cube.cuboid.algorithm.generic.lib; - -/** - * Modified from the CrossoverPolicy.java in https://github.com/apache/commons-math - * - * Policy used to create a pair of new chromosomes by performing a crossover - * operation on a source pair of chromosomes. - * - */ -public interface CrossoverPolicy { - - /** - * Perform a crossover operation on the given chromosomes. - * - * @param first the first chromosome. - * @param second the second chromosome. - * @return the pair of new chromosomes that resulted from the crossover. - * @throws IllegalArgumentException if the given chromosomes are not compatible with this {@link CrossoverPolicy} - */ - ChromosomePair crossover(Chromosome first, Chromosome second) throws IllegalArgumentException; -} diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/ElitisticListPopulation.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/ElitisticListPopulation.java deleted file mode 100755 index f35b2e2..0000000 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/ElitisticListPopulation.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -package org.apache.kylin.cube.cuboid.algorithm.generic.lib; - -import java.util.Collections; -import java.util.List; - -import org.apache.commons.math3.exception.NotPositiveException; -import org.apache.commons.math3.exception.NullArgumentException; -import org.apache.commons.math3.exception.NumberIsTooLargeException; -import org.apache.commons.math3.exception.OutOfRangeException; -import org.apache.commons.math3.exception.util.LocalizedFormats; -import org.apache.commons.math3.util.FastMath; - -/** - * Modified from the ElitisticListPopulation.java in https://github.com/apache/commons-math - * - * Population of chromosomes which uses elitism (certain percentage of the best - * chromosomes is directly copied to the next generation). - * - */ -public class ElitisticListPopulation extends ListPopulation { - - /** percentage of chromosomes copied to the next generation */ - private double elitismRate = 0.9; - - /** - * Creates a new {@link ElitisticListPopulation} instance. - * - * @param chromosomes list of chromosomes in the population - * @param populationLimit maximal size of the population - * @param elitismRate how many best chromosomes will be directly transferred to the next generation [in %] - * @throws NullArgumentException if the list of chromosomes is {@code null} - * @throws NotPositiveException if the population limit is not a positive number (< 1) - * @throws NumberIsTooLargeException if the list of chromosomes exceeds the population limit - * @throws OutOfRangeException if the elitism rate is outside the [0, 1] range - */ - public ElitisticListPopulation(final List<Chromosome> chromosomes, final int populationLimit, - final double elitismRate) - throws NullArgumentException, NotPositiveException, NumberIsTooLargeException, OutOfRangeException { - - super(chromosomes, populationLimit); - setElitismRate(elitismRate); - } - - /** - * Creates a new {@link ElitisticListPopulation} instance and initializes its inner chromosome list. - * - * @param populationLimit maximal size of the population - * @param elitismRate how many best chromosomes will be directly transferred to the next generation [in %] - * @throws NotPositiveException if the population limit is not a positive number (< 1) - * @throws OutOfRangeException if the elitism rate is outside the [0, 1] range - */ - public ElitisticListPopulation(final int populationLimit, final double elitismRate) - throws NotPositiveException, OutOfRangeException { - - super(populationLimit); - setElitismRate(elitismRate); - } - - /** - * Start the population for the next generation. The <code>{@link #elitismRate}</code> - * percents of the best chromosomes are directly copied to the next generation. - * - * @return the beginnings of the next generation. - */ - public Population nextGeneration() { - // initialize a new generation with the same parameters - ElitisticListPopulation nextGeneration = new ElitisticListPopulation(getPopulationLimit(), getElitismRate()); - - final List<Chromosome> oldChromosomes = getChromosomeList(); - Collections.sort(oldChromosomes); - - // index of the last "not good enough" chromosome - int boundIndex = (int) FastMath.ceil((1.0 - getElitismRate()) * oldChromosomes.size()); - for (int i = boundIndex; i < oldChromosomes.size(); i++) { - nextGeneration.addChromosome(oldChromosomes.get(i)); - } - return nextGeneration; - } - - /** - * Access the elitism rate. - * @return the elitism rate - */ - public double getElitismRate() { - return this.elitismRate; - } - - /** - * Sets the elitism rate, i.e. how many best chromosomes will be directly transferred to the next generation [in %]. - * - * @param elitismRate how many best chromosomes will be directly transferred to the next generation [in %] - * @throws OutOfRangeException if the elitism rate is outside the [0, 1] range - */ - public void setElitismRate(final double elitismRate) throws OutOfRangeException { - if (elitismRate < 0 || elitismRate > 1) { - throw new OutOfRangeException(LocalizedFormats.ELITISM_RATE, elitismRate, 0, 1); - } - this.elitismRate = elitismRate; - } - -} diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/Fitness.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/Fitness.java deleted file mode 100755 index 9713f25..0000000 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/Fitness.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -package org.apache.kylin.cube.cuboid.algorithm.generic.lib; - -/** - * Modified from the Fitness.java in https://github.com/apache/commons-math - * - * Fitness of a chromosome. - * - */ -public interface Fitness { - - /** - * Compute the fitness. - * @return fitness - */ - double fitness(); - -} diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/FixedGenerationCount.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/FixedGenerationCount.java deleted file mode 100755 index ff45fa2..0000000 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/FixedGenerationCount.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -package org.apache.kylin.cube.cuboid.algorithm.generic.lib; - -import org.apache.commons.math3.exception.NumberIsTooSmallException; - -/** - * Modified from the FixedGenerationCount.java in https://github.com/apache/commons-math - * - * Stops after a fixed number of generations. Each time {@link #isSatisfied(Population)} is invoked, a generation - * counter is incremented. Once the counter reaches the configured <code>maxGenerations</code> value, - * {@link #isSatisfied(Population)} returns true. - * - */ -public class FixedGenerationCount implements StoppingCondition { - /** Maximum number of generations (stopping criteria) */ - private final int maxGenerations; - /** Number of generations that have passed */ - private int numGenerations = 0; - - /** - * Create a new FixedGenerationCount instance. - * - * @param maxGenerations number of generations to evolve - * @throws NumberIsTooSmallException if the number of generations is < 1 - */ - public FixedGenerationCount(final int maxGenerations) throws NumberIsTooSmallException { - if (maxGenerations <= 0) { - throw new NumberIsTooSmallException(maxGenerations, 1, true); - } - this.maxGenerations = maxGenerations; - } - - /** - * Determine whether or not the given number of generations have passed. Increments the number of generations - * counter if the maximum has not been reached. - * - * @param population ignored (no impact on result) - * @return <code>true</code> IFF the maximum number of generations has been exceeded - */ - public boolean isSatisfied(final Population population) { - if (this.numGenerations < this.maxGenerations) { - numGenerations++; - return false; - } - return true; - } - - /** - * Returns the number of generations that have already passed. - * @return the number of generations that have passed - */ - public int getNumGenerations() { - return numGenerations; - } - -} diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/ListPopulation.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/ListPopulation.java deleted file mode 100755 index 435dc72..0000000 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/ListPopulation.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -package org.apache.kylin.cube.cuboid.algorithm.generic.lib; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import org.apache.commons.math3.exception.NotPositiveException; -import org.apache.commons.math3.exception.NullArgumentException; -import org.apache.commons.math3.exception.NumberIsTooLargeException; -import org.apache.commons.math3.exception.NumberIsTooSmallException; -import org.apache.commons.math3.exception.util.LocalizedFormats; - -/** - * Modified from the ListPopulation.java in https://github.com/apache/commons-math - * - * Population of chromosomes represented by a {@link List}. - * - */ -public abstract class ListPopulation implements Population { - - /** List of chromosomes */ - private List<Chromosome> chromosomes; - - /** maximal size of the population */ - private int populationLimit; - - /** - * Creates a new ListPopulation instance and initializes its inner chromosome list. - * - * @param populationLimit maximal size of the population - * @throws NotPositiveException if the population limit is not a positive number (< 1) - */ - public ListPopulation(final int populationLimit) throws NotPositiveException { - this(Collections.<Chromosome> emptyList(), populationLimit); - } - - /** - * Creates a new ListPopulation instance. - * <p> - * Note: the chromosomes of the specified list are added to the population. - * - * @param chromosomes list of chromosomes to be added to the population - * @param populationLimit maximal size of the population - * @throws NullArgumentException if the list of chromosomes is {@code null} - * @throws NotPositiveException if the population limit is not a positive number (< 1) - * @throws NumberIsTooLargeException if the list of chromosomes exceeds the population limit - */ - public ListPopulation(final List<Chromosome> chromosomes, final int populationLimit) - throws NullArgumentException, NotPositiveException, NumberIsTooLargeException { - - if (chromosomes == null) { - throw new NullArgumentException(); - } - if (populationLimit <= 0) { - throw new NotPositiveException(LocalizedFormats.POPULATION_LIMIT_NOT_POSITIVE, populationLimit); - } - if (chromosomes.size() > populationLimit) { - throw new NumberIsTooLargeException(LocalizedFormats.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE, - chromosomes.size(), populationLimit, false); - } - this.populationLimit = populationLimit; - this.chromosomes = new ArrayList<Chromosome>(populationLimit); - this.chromosomes.addAll(chromosomes); - } - - /** - * Add a {@link Collection} of chromosomes to this {@link Population}. - * @param chromosomeColl a {@link Collection} of chromosomes - * @throws NumberIsTooLargeException if the population would exceed the population limit when - * adding this chromosome - * @since 3.1 - */ - public void addChromosomes(final Collection<Chromosome> chromosomeColl) throws NumberIsTooLargeException { - if (chromosomes.size() + chromosomeColl.size() > populationLimit) { - throw new NumberIsTooLargeException(LocalizedFormats.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE, - chromosomes.size(), populationLimit, false); - } - this.chromosomes.addAll(chromosomeColl); - } - - /** - * Returns an unmodifiable list of the chromosomes in this population. - * @return the unmodifiable list of chromosomes - */ - public List<Chromosome> getChromosomes() { - return Collections.unmodifiableList(chromosomes); - } - - /** - * Sets the list of chromosomes. - * <p> - * Note: this method removes all existing chromosomes in the population and adds all chromosomes - * of the specified list to the population. - * - * @param chromosomes the list of chromosomes - * @throws NullArgumentException if the list of chromosomes is {@code null} - * @throws NumberIsTooLargeException if the list of chromosomes exceeds the population limit - * @deprecated use {@link #addChromosomes(Collection)} instead - */ - @Deprecated - public void setChromosomes(final List<Chromosome> chromosomes) - throws NullArgumentException, NumberIsTooLargeException { - - if (chromosomes == null) { - throw new NullArgumentException(); - } - if (chromosomes.size() > populationLimit) { - throw new NumberIsTooLargeException(LocalizedFormats.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE, - chromosomes.size(), populationLimit, false); - } - this.chromosomes.clear(); - this.chromosomes.addAll(chromosomes); - } - - /** - * Access the list of chromosomes. - * @return the list of chromosomes - * @since 3.1 - */ - protected List<Chromosome> getChromosomeList() { - return chromosomes; - } - - /** - * Add the given chromosome to the population. - * - * @param chromosome the chromosome to add. - * @throws NumberIsTooLargeException if the population would exceed the {@code populationLimit} after - * adding this chromosome - */ - public void addChromosome(final Chromosome chromosome) throws NumberIsTooLargeException { - if (chromosomes.size() >= populationLimit) { - throw new NumberIsTooLargeException(LocalizedFormats.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE, - chromosomes.size(), populationLimit, false); - } - this.chromosomes.add(chromosome); - } - - /** - * Access the fittest chromosome in this population. - * @return the fittest chromosome. - */ - public Chromosome getFittestChromosome() { - // best so far - Chromosome bestChromosome = this.chromosomes.get(0); - for (Chromosome chromosome : this.chromosomes) { - if (chromosome.compareTo(bestChromosome) > 0) { - // better chromosome found - bestChromosome = chromosome; - } - } - return bestChromosome; - } - - /** - * Access the maximum population size. - * @return the maximum population size. - */ - public int getPopulationLimit() { - return this.populationLimit; - } - - /** - * Sets the maximal population size. - * @param populationLimit maximal population size. - * @throws NotPositiveException if the population limit is not a positive number (< 1) - * @throws NumberIsTooSmallException if the new population size is smaller than the current number - * of chromosomes in the population - */ - public void setPopulationLimit(final int populationLimit) throws NotPositiveException, NumberIsTooSmallException { - if (populationLimit <= 0) { - throw new NotPositiveException(LocalizedFormats.POPULATION_LIMIT_NOT_POSITIVE, populationLimit); - } - if (populationLimit < chromosomes.size()) { - throw new NumberIsTooSmallException(populationLimit, chromosomes.size(), true); - } - this.populationLimit = populationLimit; - } - - /** - * Access the current population size. - * @return the current population size. - */ - public int getPopulationSize() { - return this.chromosomes.size(); - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return this.chromosomes.toString(); - } - - /** - * Returns an iterator over the unmodifiable list of chromosomes. - * <p>Any call to {@link Iterator#remove()} will result in a {@link UnsupportedOperationException}.</p> - * - * @return chromosome iterator - */ - public Iterator<Chromosome> iterator() { - return getChromosomes().iterator(); - } -} diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/MutationPolicy.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/MutationPolicy.java deleted file mode 100755 index 183483a..0000000 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/MutationPolicy.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -package org.apache.kylin.cube.cuboid.algorithm.generic.lib; - -/** - * Modified from the MutationPolicy.java in https://github.com/apache/commons-math - * - * Algorithm used to mutate a chromosome. - * - */ -public interface MutationPolicy { - - /** - * Mutate the given chromosome. - * @param original the original chromosome. - * @return the mutated chromosome. - * @throws IllegalArgumentException if the given chromosome is not compatible with this {@link MutationPolicy} - */ - Chromosome mutate(Chromosome original) throws IllegalArgumentException; -} diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/Population.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/Population.java deleted file mode 100755 index c22f518..0000000 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/Population.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -package org.apache.kylin.cube.cuboid.algorithm.generic.lib; - -import org.apache.commons.math3.exception.NumberIsTooLargeException; - -/** - * Modified from the Population.java in https://github.com/apache/commons-math - * - * A collection of chromosomes that facilitates generational evolution. - * - */ -public interface Population extends Iterable<Chromosome> { - /** - * Access the current population size. - * @return the current population size. - */ - int getPopulationSize(); - - /** - * Access the maximum population size. - * @return the maximum population size. - */ - int getPopulationLimit(); - - /** - * Start the population for the next generation. - * @return the beginnings of the next generation. - */ - Population nextGeneration(); - - /** - * Add the given chromosome to the population. - * @param chromosome the chromosome to add. - * @throws NumberIsTooLargeException if the population would exceed the population limit when adding - * this chromosome - */ - void addChromosome(Chromosome chromosome) throws NumberIsTooLargeException; - - /** - * Access the fittest chromosome in this population. - * @return the fittest chromosome. - */ - Chromosome getFittestChromosome(); -} diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/SelectionPolicy.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/SelectionPolicy.java deleted file mode 100755 index 7ac14c5..0000000 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/SelectionPolicy.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -package org.apache.kylin.cube.cuboid.algorithm.generic.lib; - -/** - * Modified from the SelectionPolicy.java in https://github.com/apache/commons-math - * - * Algorithm used to select a chromosome pair from a population. - * - */ -public interface SelectionPolicy { - /** - * Select two chromosomes from the population. - * @param population the population from which the chromosomes are choosen. - * @return the selected chromosomes. - * @throws IllegalArgumentException if the population is not compatible with this {@link SelectionPolicy} - */ - ChromosomePair select(Population population) throws IllegalArgumentException; -} diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/StoppingCondition.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/StoppingCondition.java deleted file mode 100755 index bf3a26d..0000000 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/StoppingCondition.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -package org.apache.kylin.cube.cuboid.algorithm.generic.lib; - -/** - * Modified from the StoppingCondition.java in https://github.com/apache/commons-math - * - * Algorithm used to determine when to stop evolution. - * - */ -public interface StoppingCondition { - /** - * Determine whether or not the given population satisfies the stopping condition. - * - * @param population the population to test. - * @return <code>true</code> if this stopping condition is met by the given population, - * <code>false</code> otherwise. - */ - boolean isSatisfied(Population population); -} diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/TournamentSelection.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/TournamentSelection.java deleted file mode 100755 index 158ccd2..0000000 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/generic/lib/TournamentSelection.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -package org.apache.kylin.cube.cuboid.algorithm.generic.lib; - -import java.util.List; - -import org.apache.kylin.cube.cuboid.algorithm.generic.GeneticAlgorithm; - -import com.google.common.collect.Lists; - -/** - * Modified from the TournamentSelection.java in https://github.com/apache/commons-math - * - * Tournament selection scheme. Each of the two selected chromosomes is selected - * based on n-ary tournament -- this is done by drawing {@link #arity} random - * chromosomes without replacement from the population, and then selecting the - * fittest chromosome among them. - * - */ -public class TournamentSelection implements SelectionPolicy { - - /** number of chromosomes included in the tournament selections */ - private int arity; - - /** - * Creates a new TournamentSelection instance. - * - * @param arity how many chromosomes will be drawn to the tournament - */ - public TournamentSelection(final int arity) { - this.arity = arity; - } - - /** - * Select two chromosomes from the population. Each of the two selected - * chromosomes is selected based on n-ary tournament -- this is done by - * drawing {@link #arity} random chromosomes without replacement from the - * population, and then selecting the fittest chromosome among them. - * - * @param population the population from which the chromosomes are chosen. - * @return the selected chromosomes. - * @throws IllegalArgumentException if the tournament arity is bigger than the population size - */ - public ChromosomePair select(final Population population) throws IllegalArgumentException { - return new ChromosomePair(tournament((ListPopulation) population), tournament((ListPopulation) population)); - } - - /** - * Helper for {@link #select(Population)}. Draw {@link #arity} random chromosomes without replacement from the - * population, and then select the fittest chromosome among them. - * - * @param population the population from which the chromosomes are chosen. - * @return the selected chromosome. - * @throws IllegalArgumentException if the tournament arity is bigger than the population size - */ - private Chromosome tournament(final ListPopulation population) throws IllegalArgumentException { - if (population.getPopulationSize() < this.arity) { - throw new IllegalArgumentException("Tournament arty is too large."); - } - // auxiliary population - ListPopulation tournamentPopulation = new ListPopulation(this.arity) { - /** {@inheritDoc} */ - public Population nextGeneration() { - // not useful here - return null; - } - }; - - // create a copy of the chromosome list - List<Chromosome> chromosomes = Lists.newArrayList(population.getChromosomes()); - for (int i = 0; i < this.arity; i++) { - // select a random individual and add it to the tournament - int rind = GeneticAlgorithm.RANDGEN.get().nextInt(chromosomes.size()); - tournamentPopulation.addChromosome(chromosomes.get(rind)); - // do not select it again - chromosomes.remove(rind); - } - // the winner takes it all - return tournamentPopulation.getFittestChromosome(); - } - - /** - * Gets the arity (number of chromosomes drawn to the tournament). - * - * @return arity of the tournament - */ - public int getArity() { - return arity; - } - - /** - * Sets the arity (number of chromosomes drawn to the tournament). - * - * @param arity arity of the tournament - */ - public void setArity(final int arity) { - this.arity = arity; - } - -} diff --git a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/greedy/GreedyAlgorithm.java b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/greedy/GreedyAlgorithm.java index 5dbe189..0f2dcc3 100755 --- a/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/greedy/GreedyAlgorithm.java +++ b/core-cube/src/main/java/org/apache/kylin/cube/cuboid/algorithm/greedy/GreedyAlgorithm.java @@ -18,13 +18,10 @@ package org.apache.kylin.cube.cuboid.algorithm.greedy; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicReference; - +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.apache.kylin.cube.cuboid.algorithm.AbstractRecommendAlgorithm; import org.apache.kylin.cube.cuboid.algorithm.BenefitPolicy; import org.apache.kylin.cube.cuboid.algorithm.CuboidBenefitModel; @@ -32,15 +29,17 @@ import org.apache.kylin.cube.cuboid.algorithm.CuboidStats; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Preconditions; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; /** -* A simple implementation of the Greedy Algorithm , it chooses the cuboids which give -* the greatest benefit based on expansion rate and time limitation. -*/ + * A simple implementation of the Greedy Algorithm , it chooses the cuboids which give + * the greatest benefit based on expansion rate and time limitation. + */ public class GreedyAlgorithm extends AbstractRecommendAlgorithm { private static final Logger logger = LoggerFactory.getLogger(GreedyAlgorithm.class); @@ -55,31 +54,23 @@ public class GreedyAlgorithm extends AbstractRecommendAlgorithm { } @Override - public List<Long> recommend(double expansionRate) { - double spaceLimit = getCuboidStats().getBaseCuboidSize() * expansionRate; - return start(spaceLimit); - } - - @Override public List<Long> start(double spaceLimit) { logger.info("Greedy Algorithm started."); executor = Executors.newFixedThreadPool(THREAD_NUM, new ThreadFactoryBuilder().setNameFormat("greedy-algorithm-benefit-calculator-pool-%d").build()); - getBenefitPolicy().initBeforeStart(); - //Initial mandatory cuboids selected.clear(); double remainingSpace = spaceLimit; - for (Long mandatoryOne : getCuboidStats().getAllCuboidsForMandatory()) { + for (Long mandatoryOne : cuboidStats.getAllCuboidsForMandatory()) { selected.add(mandatoryOne); - if (getCuboidStats().getCuboidSize(mandatoryOne) != null) { - remainingSpace -= getCuboidStats().getCuboidSize(mandatoryOne); + if (cuboidStats.getCuboidSize(mandatoryOne) != null) { + remainingSpace -= cuboidStats.getCuboidSize(mandatoryOne); } } //Initial remaining cuboid set remaining.clear(); - remaining.addAll(getCuboidStats().getAllCuboidsForSelection()); + remaining.addAll(cuboidStats.getAllCuboidsForSelection()); long round = 0; while (true) { @@ -95,18 +86,18 @@ public class GreedyAlgorithm extends AbstractRecommendAlgorithm { // If we finally find the cuboid selected does not meet a minimum threshold of benefit (for // example, a cuboid with 0.99M roll up from a parent cuboid with 1M // rows), then we should finish the process and return - if (!getBenefitPolicy().ifEfficient(best)) { + if (!benefitPolicy.ifEfficient(best)) { break; } - remainingSpace -= getCuboidStats().getCuboidSize(best.getCuboidId()); + remainingSpace -= cuboidStats.getCuboidSize(best.getCuboidId()); // If we finally find there is no remaining space, then we should finish the process and return if (remainingSpace <= 0) { break; } selected.add(best.getCuboidId()); remaining.remove(best.getCuboidId()); - getBenefitPolicy().propagateAggregationCost(best.getCuboidId(), selected); + benefitPolicy.propagateAggregationCost(best.getCuboidId(), selected); round++; if (logger.isTraceEnabled()) { logger.trace(String.format("Recommend in round %d : %s", round, best.toString())); @@ -126,10 +117,10 @@ public class GreedyAlgorithm extends AbstractRecommendAlgorithm { logger.trace("Excluded cuboidId detail:"); for (Long cuboid : excluded) { logger.trace(String.format("cuboidId %d and Cost: %d and Space: %f", cuboid, - getCuboidStats().getCuboidQueryCost(cuboid), getCuboidStats().getCuboidSize(cuboid))); + cuboidStats.getCuboidQueryCost(cuboid), cuboidStats.getCuboidSize(cuboid))); } logger.trace("Total Space:" + (spaceLimit - remainingSpace)); - logger.trace("Space Expansion Rate:" + (spaceLimit - remainingSpace) / getCuboidStats().getBaseCuboidSize()); + logger.trace("Space Expansion Rate:" + (spaceLimit - remainingSpace) / cuboidStats.getBaseCuboidSize()); } return Lists.newArrayList(selected); } @@ -145,13 +136,13 @@ public class GreedyAlgorithm extends AbstractRecommendAlgorithm { public void run() { CuboidBenefitModel currentBest = best.get(); assert (selected.size() == selectedSize); - CuboidBenefitModel.BenefitModel benefitModel = getBenefitPolicy().calculateBenefit(cuboid, selected); + CuboidBenefitModel.BenefitModel benefitModel = benefitPolicy.calculateBenefit(cuboid, selected); if (benefitModel != null && (currentBest == null || currentBest.getBenefit() == null - || benefitModel.getBenefit() > currentBest.getBenefit())) { + || benefitModel.benefit > currentBest.getBenefit())) { while (!best.compareAndSet(currentBest, - new CuboidBenefitModel(getCuboidStats().getCuboidModel(cuboid), benefitModel))) { + new CuboidBenefitModel(cuboidStats.getCuboidModel(cuboid), benefitModel))) { currentBest = best.get(); - if (benefitModel.getBenefit() <= currentBest.getBenefit()) { + if (benefitModel.benefit <= currentBest.getBenefit()) { break; } } diff --git a/kylin-it/src/test/java/org/apache/kylin/cube/cuboid/algorithm/ITAlgorithmTestBase.java b/kylin-it/src/test/java/org/apache/kylin/cube/cuboid/algorithm/ITAlgorithmTestBase.java index d8cc4e8..797d0db 100755 --- a/kylin-it/src/test/java/org/apache/kylin/cube/cuboid/algorithm/ITAlgorithmTestBase.java +++ b/kylin-it/src/test/java/org/apache/kylin/cube/cuboid/algorithm/ITAlgorithmTestBase.java @@ -30,7 +30,6 @@ import org.apache.kylin.cube.cuboid.TreeCuboidScheduler.CuboidCostComparator; import org.apache.kylin.cube.cuboid.TreeCuboidScheduler.CuboidTree; import org.junit.After; import org.junit.Before; -import org.junit.Test; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -57,14 +56,6 @@ public class ITAlgorithmTestBase { public void after() throws Exception { } - @Test - public void testMandatoryRowCountEstimation() { - for (long mandatoryCuboid : mandatoryCuboids) { - System.out.println("Cuboid id: " + mandatoryCuboid + "; Row Count: " - + cuboidStats.getStatistics().get(mandatoryCuboid)); - } - } - /** better if closer to 1, worse if closer to 0*/ public double getQueryCostRatio(CuboidStats cuboidStats, List<Long> recommendList) { CuboidTree cuboidTree = CuboidTree.createFromCuboids(recommendList, diff --git a/kylin-it/src/test/java/org/apache/kylin/cube/cuboid/algorithm/ITGeneticAlgorithmTest.java b/kylin-it/src/test/java/org/apache/kylin/cube/cuboid/algorithm/ITGeneticAlgorithmTest.java index 4f60307..9ca7386 100755 --- a/kylin-it/src/test/java/org/apache/kylin/cube/cuboid/algorithm/ITGeneticAlgorithmTest.java +++ b/kylin-it/src/test/java/org/apache/kylin/cube/cuboid/algorithm/ITGeneticAlgorithmTest.java @@ -18,15 +18,54 @@ package org.apache.kylin.cube.cuboid.algorithm; -import java.util.List; - +import com.google.common.collect.Sets; +import org.apache.commons.math3.genetics.Chromosome; +import org.apache.kylin.cube.cuboid.algorithm.generic.BitsChromosome; +import org.apache.kylin.cube.cuboid.algorithm.generic.BitsChromosomeHelper; import org.apache.kylin.cube.cuboid.algorithm.generic.GeneticAlgorithm; import org.junit.Test; +import java.util.BitSet; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; + //@Ignore("testBPUSCalculator() is unsable; whole test takes too long") public class ITGeneticAlgorithmTest extends ITAlgorithmTestBase { @Test + public void testChromosomeIsSame() { + BenefitPolicy benefitPolicy = new BPUSCalculator(cuboidStats); + + double maxSpaceLimit = cuboidStats.getBaseCuboidSize() * 10; + BitsChromosomeHelper helper = new BitsChromosomeHelper(maxSpaceLimit, cuboidStats); + + double maxSpaceLimit1 = cuboidStats.getBaseCuboidSize() * 12; + BitsChromosomeHelper helper1 = new BitsChromosomeHelper(maxSpaceLimit1, cuboidStats); + + BitSet representation = new BitSet(); + representation.set(10); + Chromosome chromosome = new BitsChromosome(representation, benefitPolicy, helper); + Set<Chromosome> chromosomeSet = Sets.newHashSet(chromosome); + + BitSet representation1 = new BitSet(); + representation1.set(10); + chromosomeSet.add(((BitsChromosome) chromosome).newBitsChromosome(representation1)); + assertEquals(1, chromosomeSet.size()); + + BitSet representation2 = new BitSet(); + representation2.set(12); + chromosomeSet.add(((BitsChromosome) chromosome).newBitsChromosome(representation2)); + assertEquals(2, chromosomeSet.size()); + + BitSet representation3 = new BitSet(); + representation3.set(12); + chromosomeSet.add(new BitsChromosome(representation3, benefitPolicy, helper1)); + assertEquals(2, chromosomeSet.size()); + } + + @Test public void testBPUSCalculator() { BenefitPolicy benefitPolicy = new BPUSCalculator(cuboidStats); GeneticAlgorithm algorithm = new GeneticAlgorithm(-1, benefitPolicy, cuboidStats); -- To stop receiving notification emails like this one, please contact nju_y...@apache.org.